.htaccess Generator for Apache 2.4

Tick boxes, get a correct .htaccess: 301 redirects, HTTPS + www force, error pages, caching, gzip, htpasswd, IP rules, hotlink protection, PHP flags.

Presets
Your domain

301-redirects every HTTP request to the same URL over HTTPS. Enable the host rule below too and both happen in one redirect. Behind Cloudflare/a proxy? Read the emitted comment — you may need the X-Forwarded-Proto variant to avoid a loop.

Picks one host so example.com and www.example.com don't serve duplicate content. Other subdomains are left alone.

exact page matches one URL (anchored); whole section moves /dir and everything under it to the new prefix.

Local paths only — an external URL turns the error into a 302 and the status code is lost.

mod_expires TTLs for CSS/JS/images/fonts; HTML stays uncached. The immutable option adds a Cache-Control rule for bundler-style hashed filenames.

mod_deflate for text types — HTML, CSS, JS, JSON, SVG. Images and woff/woff2 are deliberately excluded: already compressed.

Create the file with htpasswd -c -B /path/.htpasswd user — the emitted comments include the commands.

Allow-list → <RequireAny> (first match wins). Deny-list → <RequireAll> with Require all granted + not ip, the only correct 2.4 form.

allow blank Referer (direct visitors & privacy tools)

On PHP-FPM hosts this block is skipped by the guard — set the same keys in .user.ini instead.

The classic !-f !-d → index.php block. Only for sites without WordPress's own block — WP already writes it.

Options -Indexes — returns 403 instead of listing files. The most common AllowOverride blocker; delete the lines if the server 500s.

.htaccess

What you get that a snippet site doesn’t give you

Most .htaccess snippets floating around the web are Apache 2.2-era, half of them syntax-error on a modern server, and almost none say why the lines are shaped that way. This generator emits Apache 2.4 directives with the guards and ordering already correct: every feature sits in the <IfModule> block for the module it needs, so a host without mod_php or mod_expires degrades to “feature off” instead of a 500 error page. Comments inside the output explain each block and flag the traps — proxy redirect loops, htpasswd paths that leak, the Require not ip rule that denies everybody if you write it wrong.

The pieces, honestly scoped

  • Redirects — “exact page” rows emit anchored RedirectMatch patterns, because plain Redirect 301 /old /new is a prefix match that also catches /old.bak and /oldfoo. “Whole section” rows carry /dir and everything under it to the new prefix without over-matching lookalike paths. More in the 301 redirects guide.
  • HTTPS + canonical host — combined into a single-redirect rule when both are on (one hop, not two), with the X-Forwarded-Proto caveat for CDN-fronted sites. See force HTTPS and pick one host.
  • Access control — allow-lists use <RequireAny> (first match wins); deny-lists use <RequireAll> with Require all granted + Require not ip, because a bare Require not ip can never grant access and would 403 your whole site. The password & IP guide covers htpasswd creation and combining auth with IP rules.
  • Performance — mod_deflate for text types only (JPEG/PNG/WOFF2 are already compressed and are deliberately excluded), mod_expires TTLs with an immutable Cache-Control option for fingerprinted assets. Caching & compression explains the trade-offs.
  • Debugging — if the file 500s, the 500-error guide has the ordered checklist; if you want to understand what RewriteCond actually does, start with mod_rewrite basics.

Everything is assembled in your browser — the file never crosses the network until you upload it to your server.

Frequently asked questions

Where does the generated file go?

Save it as .htaccess (a dotfile, no extension) in your site's document root — the directory your domain serves, often public_html/, htdocs/ or www/. Rules apply to that directory and everything under it. On shared hosting you usually upload it with FTP or the file manager; on your own server you may prefer putting the same directives in the VirtualHost config instead — .htaccess is re-read on every request, the main config is not.

The site started returning 500 after I uploaded the file. Now what?

A 500 right after upload means the file is being read but a directive inside is failing. The usual causes: your host doesn't allow Options overrides, a module the rules need isn't loaded (our <IfModule> guards prevent most of these), a typo introduced during paste, or AllowOverride is too restrictive. Delete the file to confirm, then re-add sections one at a time. The 500-error debugging guide walks the whole checklist.

Why wrap everything in &lt;IfModule&gt; blocks?

Because a directive for a module that isn't loaded kills the whole site with a 500 — php_value on a PHP-FPM host is the classic example. The guard makes the block simply not apply when its module is absent: the feature is off instead of the site being down. Trade-off, stated honestly: a skipped guard means the feature silently doesn't work, so always verify the behavior after uploading.

301 or 302 — and does the generator's ordering matter?

Use 301 (permanent) for moved content — search engines transfer ranking signals and browsers cache it. 302/307 are for temporary moves. Inside the file, mod_rewrite rules run before mod_alias ones regardless of order, so the canonical-host/HTTPS block always fires first; we emit it first anyway because humans read top-down.

Does this work on Nginx, LiteSpeed or Cloudflare?

Nginx ignores .htaccess entirely — you'd translate the rules into nginx.conf (return 301, location blocks). LiteSpeed reads .htaccess for rewrites but not every directive. Cloudflare can do redirects and HTTPS at the edge before the request reaches Apache, which is often the better place. The output here targets real Apache 2.4 with standard modules.

Is it safe to paste these rules into WordPress's .htaccess?

Mostly yes — the rules here play fine alongside WordPress's own # BEGIN WordPress block (keep that block; our WordPress front controller toggle is for sites without it). Put canonicalization/redirects before it. Never edit inside the BEGIN/END markers — WordPress rewrites that section.

Why does the htpasswd path have to be absolute and outside the web root?

AuthUserFile takes a filesystem path, not a URL — a relative one resolves against ServerRoot and just won't find your file. Outside the web root matters because a .htpasswd inside public_html can be downloaded by anyone: it holds your password hashes. Generate it with htpasswd -c -B /home/you/.htpasswd user. More in the password protection guide.

Does anything get uploaded when I use this page?

No. The file is assembled by JavaScript in your browser tab — check the network panel and you'll see zero requests after load. Your domain, IPs and paths never leave the device.

Latest articles