Skip to content
IsMyENVPublic

Web servers · 7 min read

How to block .env files in Apache

Apache 2.4 examples for denying .env and other dotfiles in the virtual host or .htaccess, including AllowOverride pitfalls and how to verify the rule.

Last updated

Start with the DocumentRoot

Apache serves every readable file below DocumentRoot. If that is your project directory, the .env file next to your code is public. Point the virtual host at the application's public directory instead:

/etc/apache2/sites-available/example.com.confapache
<VirtualHost *:443>
    ServerName example.com
    DocumentRoot /var/www/example-app/public

    <Directory /var/www/example-app/public>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # SSL directives omitted for brevity
</VirtualHost>

Options -Indexes also disables directory listings, which would otherwise reveal file names.

Rule for the virtual host (recommended)

Rules in the server configuration are faster than .htaccess files and can't be switched off by an upload. Add both of the following inside each <VirtualHost> (or globally in apache2.conf/httpd.conf):

inside <VirtualHost>apache
# 1) Deny environment files by name, in any directory.
<FilesMatch "^\.env">
    Require all denied
</FilesMatch>

# 2) Answer 404 for every hidden file or directory (.git/, .svn/, ...)
#    except /.well-known/, which ACME and security.txt need.
RedirectMatch 404 "/\.(?!well-known/)"
  • <FilesMatch> compares the regular expression with the file name only, so ^\.env matches .env, .env.local and .env.production in every directory. It doesn't match files inside hidden directories such as .git/config, which is why the second rule exists.
  • RedirectMatch 404 (from mod_alias) compares the regex with the URL path and responds with 404 Not Found. With a status outside 3xx, no target URL is given.
  • Debian and Ubuntu already ship <FilesMatch "^\.ht"> for .htaccess and .htpasswd. That rule doesn't cover .env.

Rule for .htaccess (shared hosting)

If you can't edit the server configuration, put the same directives into the .htaccess file in your web root. On shared hosting where the whole application lives in public_html, this rule is essential:

public_html/.htaccessapache
<FilesMatch "^\.env">
    Require all denied
</FilesMatch>

RedirectMatch 404 "/\.(?!well-known/)"

Keep any existing rewrite rules (for example the front-controller rules shipped by Laravel, WordPress or Symfony) below these lines. LiteSpeed-based hosting also reads .htaccess files and understands these directives in typical setups. Confirm with a test request.

The AllowOverride trap

.htaccess rules are silently ignored if overrides are disabled

Since Apache 2.3.9, AllowOverride defaults to None. In that case Apache doesn't even read .htaccess, and your protection doesn't exist, without any error message.

The directives above need these override classes for the directory: AuthConfig for Require and FileInfo for RedirectMatch. AllowOverride All includes both. Better yet, move the rules into the virtual host, where they don't depend on overrides at all.

Apache 2.2 syntax

Very old servers use the pre-2.4 access syntax:

Apache 2.2 onlyapache
<FilesMatch "^\.env">
    Order allow,deny
    Deny from all
</FilesMatch>

On Apache 2.4, these directives only work with mod_access_compat, and mixing old and new syntax in the same context leads to confusing results. Use Require all denied on 2.4, and plan an upgrade if you are still on 2.2, which reached end of life in 2017.

Test and reload

Shell (Debian/Ubuntu)bash
sudo apachectl configtest        # "Syntax OK"
sudo systemctl reload apache2    # httpd on RHEL/Fedora

curl -sS -o /dev/null -w "%{http_code}\n" https://example.com/.env          # 403 or 404
curl -sS -o /dev/null -w "%{http_code}\n" https://example.com/.env.local    # 403 or 404
curl -sS -o /dev/null -w "%{http_code}\n" https://example.com/.git/config   # 404

Finally, run the IsMyENVPublic check. If the file was reachable before the fix, follow the incident steps and rotate its secrets. For Nginx setups, see the Nginx guide.

Remediation checklist

  • DocumentRoot points to the public directory, not the project root
  • <FilesMatch "^\.env"> with Require all denied is active
  • Hidden directories such as .git/ return 404, while /.well-known/ still works
  • If you rely on .htaccess: AllowOverride permits AuthConfig and FileInfo
  • Options -Indexes disables directory listings
  • apachectl configtest passes and Apache was reloaded
  • If the file was exposed: all credentials were rotated

Frameworks

Laravel .env security

Point the document root at public/, disable debug mode, cache config and rotate APP_KEY safely.

9 min read