</>
DevToolHub
Developer workspace with code and keyboard on a desk

.htaccess File Explained: What It Does and How to Use It

·9 min read
Quick answer: The .htaccess file is a configuration file for Apache web servers. It sits in your website's root directory (or any subdirectory) and controls redirects, URL rewriting, access permissions, caching, and error pages --- all without touching the main server config. Changes take effect immediately, no server restart needed.

The first time I edited a .htaccess file, I accidentally locked myself out of my own website. One misplaced Deny from all directive and the entire site returned 403 Forbidden. I stared at a blank browser tab for 20 minutes before I figured out what happened.

That experience taught me something: .htaccess is extremely powerful and completely unforgiving. There's no syntax highlighting, no error messages in the browser, and one wrong line breaks everything. But once you understand what each directive does, it's the single most useful file on an Apache server.

Where Does .htaccess Go?

The .htaccess file lives in your website's document root --- the same folder as your index.html or index.php. On most shared hosting accounts, that's /public_html/ or /var/www/html/.

/public_html/
  .htaccess          ← main site config
  index.html
  /blog/
    .htaccess        ← blog-specific overrides (optional)
  /admin/
    .htaccess        ← admin-specific rules (optional)

Key rules about .htaccess placement:

  • It affects its directory and all subdirectories. A rule in /public_html/.htaccess applies to the entire site. A rule in /public_html/admin/.htaccess only applies to the /admin/ directory and anything inside it.
  • Subdirectory files override parent files. If the root .htaccess allows access from all IPs but /admin/.htaccess restricts it to one IP, the restriction wins for the /admin/ folder.
  • The dot prefix means it's hidden. On Linux/macOS, files starting with . don't show up in normal directory listings. Use ls -la in terminal or enable "show hidden files" in your FTP client. I've seen people create a file called htaccess (no dot) and wonder why nothing works.
  • It must be plain text with Unix line endings. Windows Notepad adds \r\n line endings that can silently break directives. Use VS Code, Sublime, or any editor that saves with \n line endings.

What .htaccess Can Control

Here's the full scope. Most people only know about redirects, but .htaccess handles far more.

CategoryWhat It DoesExample Use Case
RedirectsSend visitors from one URL to anotherOld page moved to new URL (301 redirect)
URL RewritingTransform ugly URLs into clean ones/product.php?id=42 displays as /products/widget
Access ControlAllow or block specific IPs or user agentsBlock known spam bots, restrict admin panel by IP
AuthenticationRequire username/password for a directoryPassword-protect a staging site or admin folder
Custom Error PagesShow branded pages for 404, 500, etc.Custom 404 page instead of the default Apache error
CachingSet browser cache duration for file typesCache images for 1 year, CSS/JS for 1 month
CompressionEnable gzip/deflate for text-based filesCompress HTML, CSS, JS to reduce page load time
MIME TypesDefine how the server handles file extensionsServe .webp images with the correct content type
Security HeadersAdd HTTP security headersX-Frame-Options, Content-Security-Policy, HSTS
Directory OptionsControl directory listing and file executionDisable directory browsing, prevent PHP execution in uploads
That's a lot of power in one file. The flip side: a broken .htaccess can make your entire site unreachable, redirect into infinite loops, or silently serve wrong content. Always keep a backup of the working version before making changes.

The Most Common Directives (With Code)

Here are the directives you'll use 90% of the time, with copy-paste examples.

Redirects

# 301 permanent redirect (single page)
Redirect 301 /old-page https://example.com/new-page

# 301 redirect entire site to new domain
RewriteEngine On
RewriteCond %{HTTP_HOST} ^olddomain\.com$ [NC]
RewriteRule ^(.*)$ https://newdomain.com/$1 [R=301,L]

The 301 tells browsers and search engines this move is permanent. Google transfers ranking signals from the old URL to the new one. Use 302 only for temporary moves (A/B tests, maintenance pages). For a deeper dive, read the htaccess redirect guide.

Force HTTPS

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

This catches every HTTP request and redirects it to HTTPS. The [L] flag means "stop processing rules after this one." Without it, subsequent rules can interfere and cause redirect loops. Every site should have this rule --- Google uses HTTPS as a ranking signal, and Chrome flags HTTP as "Not Secure."

Force WWW or Non-WWW

# Force www
RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

# Force non-www
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]

Pick one and stick with it. Having both www.example.com and example.com resolve means Google sees every page as duplicate content. I prefer non-www --- shorter, cleaner, and there's no technical advantage to the www prefix on modern hosting.

Custom Error Pages

ErrorDocument 404 /404.html
ErrorDocument 500 /500.html
ErrorDocument 403 /403.html

The path is relative to your document root. Make sure the file actually exists --- an ErrorDocument pointing to a missing file creates a recursion that Apache handles ungracefully. Your 404 page should include navigation, a search bar, and links to popular pages. A good 404 recovers the visit; the default Apache 404 loses it forever.

Browser Caching

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType text/css "access plus 1 month"
  ExpiresByType application/javascript "access plus 1 month"
  ExpiresByType text/html "access plus 0 seconds"
</IfModule>

This tells browsers how long to cache each file type. Images rarely change --- cache them for a year. CSS and JS change more often --- one month. HTML should never be cached (or cached very briefly) so users always see fresh content. These rules directly improve your Lighthouse and Core Web Vitals scores.

Enable Gzip Compression

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/plain text/xml
  AddOutputFilterByType DEFLATE text/css application/javascript
  AddOutputFilterByType DEFLATE application/json application/xml
</IfModule>

Gzip compression reduces file sizes by 60-80% for text-based assets. A 100KB HTML page becomes 20-30KB over the wire. This is one of the easiest performance wins you can get --- most shared hosting has mod_deflate enabled, you just need to activate it.

Block Bad Bots

RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (SemrushBot|AhrefsBot|MJ12bot|DotBot) [NC]
RewriteRule .* - [F,L]

This returns a 403 Forbidden to common SEO crawler bots that hammer your server without adding value. Note: only block bots you're sure you don't need. Blocking Googlebot or Bingbot will remove you from search results. I keep a running list and add to it whenever I spot a bot eating bandwidth in my server logs.

.htaccess vs Server Config vs Application-Level Routing

A common question: should this live in .htaccess or somewhere else?

ApproachSpeedFlexibilityWhen to Use
.htaccessSlowest (read on every request)Per-directory controlShared hosting, no server config access
httpd.conf / apache2.confFastest (loaded once at startup)Server-wide onlyVPS/dedicated server with root access
Application routing (Next.js, Express, etc.)MediumFull programmatic controlModern frameworks, Vercel, Netlify
CDN rules (Cloudflare, Vercel)Fastest (edge-level)Limited to what the CDN supportsPerformance-critical redirects
On shared hosting, .htaccess is your only option. Apache reads it on every single request, which adds a few milliseconds of overhead. On a VPS, move your rules into httpd.conf and disable .htaccess entirely (AllowOverride None) for better performance.

If you're using Next.js on Vercel (like this site), you don't have Apache at all. Redirects go in next.config.js or vercel.json. But if you manage any Apache-hosted sites --- WordPress, Laravel, plain PHP --- .htaccess is still the workhorse.

Don't want to write these rules by hand? The htaccess generator builds production-ready directives for redirects, HTTPS enforcement, caching, compression, and security headers. Select what you need, copy the output, paste it into your file.

How to Debug .htaccess Problems

When something goes wrong (and it will), here's my debugging process:

1. Check the error log. SSH into your server and run tail -f /var/log/apache2/error.log (Debian/Ubuntu) or tail -f /var/log/httpd/error_log (CentOS/RHEL). The error log tells you exactly which line is broken.

2. Comment out everything and add rules back one at a time. Put a # at the start of every line, then uncomment them in blocks. When the site breaks, you've found your problem rule.

3. Check if mod_rewrite is enabled. Most RewriteRule directives require mod_rewrite. Run apachectl -M | grep rewrite on your server. If it's not listed, enable it with a2enmod rewrite and restart Apache.

4. Verify AllowOverride is set correctly. If your server config has AllowOverride None, Apache ignores .htaccess entirely. You need at least AllowOverride All in your virtual host config for .htaccess to work.

5. Watch for infinite redirect loops. If your browser says "too many redirects," you likely have two rules fighting each other. Classic example: one rule forces HTTPS, another forces non-www, but they're in the wrong order or missing proper conditions. The HTTPS rule should always come first.

FAQ

Does Nginx use .htaccess?

No. .htaccess is Apache-only. Nginx uses a different configuration format in files like /etc/nginx/nginx.conf or site-specific configs in /etc/nginx/sites-available/. There's no per-directory override mechanism in Nginx --- all config must be in the main server block. If you're migrating from Apache to Nginx, every .htaccess rule needs to be manually translated. Tools like winginx.com/en/htaccess can help with the conversion.

Can .htaccess break my entire site?

Yes, instantly. A single syntax error makes Apache return a 500 Internal Server Error for every page. That's why you should always keep an FTP client open (or SSH session ready) when editing .htaccess live. If the site goes down, you can immediately revert the file. Better yet: test changes on a staging environment first, or keep a backup copy named .htaccess.backup in the same directory.

Is .htaccess a security risk?

The file itself isn't risky --- but mistakes in it are. A misconfigured Allow from all directive can expose sensitive directories. And if someone gains write access to your .htaccess, they can redirect your entire site to a phishing page. Protect it with chmod 644 (owner can read/write, everyone else read-only) and add a rule to block HTTP access to the file itself: <Files .htaccess> Require all denied </Files>.

How many .htaccess files can I have?

As many as you have directories. Each directory can have its own .htaccess file. Apache processes them top-down: root first, then subdirectories. In practice, most sites need just one in the document root. Multiple .htaccess files add complexity and make debugging harder. I only use subdirectory files for specific cases like password-protecting an admin panel or blocking PHP execution in an uploads folder.

Next Steps

  • Generate production-ready .htaccess rules without memorizing syntax using the htaccess generator.
  • Learn how to set up redirects --- 301, 302, regex patterns, and common migration scenarios --- in the htaccess redirect guide.
  • Make sure your URLs follow SEO best practices before configuring rewrites. Read what is a URL slug and the slug generator for clean URL formatting.