.htaccess Generator

Assemble an Apache .htaccess from checkboxes — rewrites, hardening, caching and the WordPress block — then copy it out.

Runs on your deviceWordPressWorkingNo account needed

What this tool does

.htaccess Generator writes a complete root file from a set of settings rather than from anything you upload. Each block it emits is a named, commented section: Options -Indexes for directory listings, a single rewrite that folds the HTTPS and canonical-host redirects into one hop, deny rules for xmlrpc.php and for wp-config.php, debug.log, error_log, readme.html, license.txt and the dotfiles .env, .git*, .htpasswd and .htaccess itself, then mod_headers, mod_deflate and mod_expires blocks. The WordPress rewrite block is always written last, in the standard single-site form or either multisite variant.

Two details that are easy to get wrong by hand are handled for you. Every deny block is emitted twice — Require all denied inside an mod_authz_core guard for Apache 2.4, and Order deny,allow with Deny from all for 2.2 — so the file behaves the same on either version, and every block that needs a module carries its own IfModule guard, so an absent mod_expires or mod_deflate is skipped instead of returning an error. Options -Indexes is the exception: it needs no module, but it does need AllowOverride Options, and a host that withholds that answers with a 500. The rule that stops PHP running inside wp-content/uploads is deliberately kept out of the main output and printed under a marked divider, because it is a second, separate file: pasted into the root it would deny index.php and take the site down.

Everything is assembled in this browser tab from the settings you choose, so nothing is uploaded and the tool never contacts your server. That is also its main limit. It cannot read your current file, cannot tell whether AllowOverride lets Apache consult .htaccess at all, and cannot test a single rule; anything you paste into the input box is carried through verbatim and unchecked. Two settings are narrower than they look — the PHP limits block only applies where PHP runs as an Apache module and is ignored under PHP-FPM, and the whole file is ignored outright by nginx. Keep a copy of what you are replacing, because a directive Apache rejects returns a 500 for the entire site rather than for one page.

How to use it

  1. Set the domain and choose the WordPress block. The domain feeds the canonical redirect and the hotlink rule, and is rejected if it is not a real dot-suffixed hostname when either of those is on. Enter it without http:// or a trailing slash, then pick single site, sub-directory multisite, sub-domain multisite, or leave the block out.
  2. Decide how URLs should canonicalise. Force HTTPS and the www / non-www choice are combined into one 301 rewrite instead of two, so a visitor arriving on http://www. is not bounced twice. Set the host to Leave the host alone and the redirect keeps whatever host was asked for. One consequence of folding the two together: with Force HTTPS unticked but a canonical host chosen, the rule is written to http://, so an https visitor on the wrong host is sent back down to plain http. Leave Force HTTPS on unless you have a reason not to.
  3. Tick the hardening you want. xmlrpc.php denied, sensitive files denied, directory listing off, ?author=N enumeration blocked outside /wp-admin, the security headers block, and HSTS only once HTTPS works on every subdomain.
  4. Add performance rules and any of your own. Compression and browser caching are on by default: mod_expires gives images and fonts a year, CSS and JS a month and HTML zero seconds, and then a Cache-Control line re-stamps every static extension it matches — CSS and JS among them — at a year with immutable, so which of the two wins depends on your server's module order rather than on anything set here. Paste redirects or host-specific lines into the input box and they land in their own section above the WordPress markers.
  5. Run it, then take the file out. Copy output puts the file on the clipboard. If the uploads rule is on, the part below the divider is a second file that belongs at wp-content/uploads/.htaccess, not in the root.

Processing & privacy

Runs on your device. Your file is processed directly in this browser.

Common use cases

Rebuilding a permalink structure that stopped working

An overwritten or missing WordPress block turns every post into a 404 while the home page still loads. Regenerating the standard block puts the rewrite back.

Finishing a move to HTTPS

The certificate is installed but links still resolve over http and on www. One canonical rewrite sends both to the same address in a single redirect rather than chaining two.

Hardening an install without adding a plugin

Denying xmlrpc.php, blocking author enumeration and refusing to serve wp-config.php or debug.log are file-level rules, so they apply before PHP or WordPress is reached.

Setting caching and compression at the server

mod_expires and mod_deflate cover static assets without a caching plugin, and the headers survive that plugin being disabled or replaced.

Carrying old redirect rules into a new file

Paste your existing Redirect 301 lines into the input box so they are kept, above the markers WordPress rewrites every time you save the permalinks page.

Questions

Is my configuration sent anywhere?
No. The file is built in this browser tab from the settings you pick and any text you paste, so there is nothing to transmit and it still works with the network disconnected.
Why is the WordPress block always last?
WordPress rewrites everything between # BEGIN WordPress and # END WordPress whenever the permalinks page is saved. Anything you want to survive that has to sit above those markers, which is where every generated block and your own pasted rules are placed.
What is the second file at the bottom of the output?
The uploads rule. It denies php, phtml, phar, cgi and similar handlers and switches the rewrite engine off, and it belongs at wp-content/uploads/.htaccess. In the root file the same rule would deny index.php and stop the site loading.
The rules had no effect at all. Why?
Apache only consults .htaccess where AllowOverride permits it; under AllowOverride None it is never read and nothing reports that. nginx never reads it, so those rules have to be translated into the server block instead. LiteSpeed and OpenLiteSpeed do honour it.
My edited CSS never reaches visitors. What causes that?
The caching section ends with Cache-Control: public, max-age=31536000, immutable on every .css and .js file it matches, and immutable tells the browser not to revalidate even on a reload. That is only safe while the URL changes when the file does — WordPress's ?ver= bump on an enqueued stylesheet does that, a hand-written link to style.css does not. Untick browser caching if you edit CSS in place.
The site returned a 500 after I saved the file. What now?
Restore the copy you took, or rename the new file, and the site comes back immediately. A directive Apache does not recognise breaks the whole directory rather than one page, and while the IfModule guards cover a missing module, nothing here checks a rule you pasted yourself.
Will blocking xmlrpc.php break anything?
It breaks whatever uses it: Jetpack, the WordPress mobile apps and pingbacks. Editing in the browser is unaffected. Leave the box unticked if you depend on any of those.
Do the PHP upload limits work on every host?
No. php_value directives only apply where PHP is loaded as an Apache module, so that block sits inside an mod_php guard and is skipped under PHP-FPM, which is what most hosts now run. There the same limits belong in php.ini or .user.ini.
Should I switch HSTS on?
Only when HTTPS already works on every subdomain. The header is written with a one-year max-age and includeSubDomains, and browsers remember it, so a subdomain without a certificate becomes unreachable for months and cannot be visited over http to fix it.

Related tools