DEV Community

Cover image for Lock It Down: Securing Your NGINX Site with `.htpasswd` and Ansible
Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on

Lock It Down: Securing Your NGINX Site with `.htpasswd` and Ansible

Hi there! I'm Maneshwar. Right now, I’m building LiveAPI, a first-of-its-kind tool that helps you automatically index API endpoints across all your repositories. LiveAPI makes it easier to discover, understand, and interact with APIs in large infrastructures.


Your website is cool. But is it private?
Whether you're protecting a staging server, a Nomad dashboard, or a developer tool—Basic Auth via .htpasswd is your first line of defense.

Here's how to do it the Ansible way, with reusable roles, templated config, and one command deploys.

Quick Setup: What You'll Build

  • NGINX config auto-synced with Ansible
  • .htpasswd file created with secure credentials
  • Basic Auth enforced on protected domains
  • All configs dropped into /etc/nginx/ via rsync
  • Symlinks created in /etc/nginx/sites-enabled/
  • SSL ready for Certbot if needed

Project Structure

Start by generating the role:

ansible-galaxy init roles/nginx-conf-sync --offline
Enter fullscreen mode Exit fullscreen mode

Your structure should look like:

ansible/
├─ nginx-conf-sync-playbook.yml
├─ roles/
│  ├─ nginx-conf-sync/
│  │  ├─ files/
│  │  │  ├─ answer.dev.to
│  │  │  ├─ budi.dev.to
│  │  ├─ templates/
│  │  │  ├─ nginx.conf.j2
│  │  │  └─ options-ssl-nginx.conf.j2
│  │  ├─ tasks/
│  │  │  ├─ main.yml
│  │  │  ├─ create-nginx-confs.yml
│  │  │  ├─ certbot-setup-playbook.yml
│  │  │  └─ restart-nginx.yml
...
Enter fullscreen mode Exit fullscreen mode

Creating the .htpasswd File with Ansible

Inside roles/nginx-conf-sync/tasks/main.yml, ensure this task exists:

- name: Create .htpasswd file for basic auth
  command: htpasswd -bc /etc/nginx/.htpasswd hexmos v@6U6NYFVJ9CSk
  args:
    creates: /etc/nginx/.htpasswd
Enter fullscreen mode Exit fullscreen mode

This ensures the file is created once unless manually deleted. Want to regenerate it each time? Remove args.creates.

To clean up stale passwords before that:

- name: Remove .htpasswd file if it exists
  file:
    path: /etc/nginx/.htpasswd
    state: absent
Enter fullscreen mode Exit fullscreen mode

Add Auth to Your NGINX Config

In your nginx.conf.j2 or domain-specific confs (files/answer.dev.to, etc.), add:

server {
    server_name nomad.apps.hexmos.com;

    location / {
        auth_basic           "Administrator’s Area";
        auth_basic_user_file /etc/nginx/.htpasswd;

        proxy_pass http://localhost:4646;  # or whatever your app is
    }
}
Enter fullscreen mode Exit fullscreen mode

Ansible will rsync this into /etc/nginx/sites-available/ and symlink it.

Full Task Flow (Summary)

Your main.yml calls:

- import_tasks: create-nginx-confs.yml
- import_tasks: install-cron.yml
- import_tasks: certbot-setup-playbook.yml
- import_tasks: restart-nginx.yml
Enter fullscreen mode Exit fullscreen mode

Inside create-nginx-confs.yml, make sure you have:

- name: Ensure NGINX config directory exists
  file:
    path: "/etc/nginx/sites-available"
    state: directory

- name: Deploy main nginx.conf from template
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: "0644"

- name: Sync all nginx domain configs using rsync
  synchronize:
    src: "{{ role_path }}/files/"
    dest: "/etc/nginx/sites-available/"
    recursive: yes
    delete: yes

- name: Create symlinks in bulk
  ansible.builtin.shell: |
    for site in {{ nginx_conf_domains | join(' ') }}; do
      ln -sf "/etc/nginx/sites-available/$site" "/etc/nginx/sites-enabled/$site"
    done
Enter fullscreen mode Exit fullscreen mode

Test It

  1. Run your playbook:
ansible-playbook -i hosts.ini nginx-conf-sync-playbook.yml
Enter fullscreen mode Exit fullscreen mode
  1. Access the site in your browser.
    You’ll be prompted for a username (hexmos) and password (v@6U6NYFVJ9CSk).

  2. To reset the password:

ansible-playbook -i hosts.ini nginx-conf-sync-playbook.yml
Enter fullscreen mode Exit fullscreen mode

Just let the .htpasswd file get regenerated.

Pro Tips

  • Use htpasswd -B instead of -m to use bcrypt instead of MD5 for stronger hashing.
  • Store the username/password as Ansible Vault secrets if you're pushing this to CI.
  • Protect specific locations (like /admin) if you don’t want to auth the whole site.

That’s a Wrap

A single .htpasswd file + NGINX + Ansible = locked down and audit-friendly.
Whether you’re shipping staging sites, internal dashboards, or legacy admin panels — drop in a password wall without writing a line of shell.

Fast. Reproducible. Scriptable. Done.


LiveAPI helps you get all your backend APIs documented in a few minutes.

With LiveAPI, you can generate interactive API docs that allow users to search and execute endpoints directly from the browser.

LiveAPI Demo

If you're tired of updating Swagger manually or syncing Postman collections, give it a shot.

Top comments (0)