Reference: mod_rewrite, URL rewriting and “pretty links” explained










131















"Pretty links" is an often requested topic, but it is rarely fully explained. mod_rewrite is one way to make "pretty links", but it's complex and its syntax is very terse, hard to grok and the documentation assumes a certain level of proficiency in HTTP. Can someone explain in simple terms how "pretty links" work and how mod_rewrite can be used to create them?



Other common names, aliases, terms for clean urls: RESTful URLs, User-friendly URLs, SEO-friendly URLs, Slugging, MVC urls (probably a misnomer)










share|improve this question



















  • 1





    Slug or Slugging is another common alias/term for pretty urls.

    – Mike B
    Dec 13 '13 at 12:30







  • 2





    @Mike Sort of, but slugs are often a part of pretty URLs. A slug is pretty specifically when, for example, the headline of an article is turned into a URL-friendly form which then acts as the identifier of that article. So reference-mod-rewrite-url-rewriting-explained is the slug, /questions/20563772/reference-mod-rewrite-url-rewriting-explained is the pretty URL.

    – deceze
    Dec 13 '13 at 12:54






  • 1





    I think that the .htaccess and mod-rewrite tags should be updated to include a link to this question, as it covers much of what is asked on a regular basis. Thoughts?

    – Mike Rockétt
    Feb 13 '16 at 13:23















131















"Pretty links" is an often requested topic, but it is rarely fully explained. mod_rewrite is one way to make "pretty links", but it's complex and its syntax is very terse, hard to grok and the documentation assumes a certain level of proficiency in HTTP. Can someone explain in simple terms how "pretty links" work and how mod_rewrite can be used to create them?



Other common names, aliases, terms for clean urls: RESTful URLs, User-friendly URLs, SEO-friendly URLs, Slugging, MVC urls (probably a misnomer)










share|improve this question



















  • 1





    Slug or Slugging is another common alias/term for pretty urls.

    – Mike B
    Dec 13 '13 at 12:30







  • 2





    @Mike Sort of, but slugs are often a part of pretty URLs. A slug is pretty specifically when, for example, the headline of an article is turned into a URL-friendly form which then acts as the identifier of that article. So reference-mod-rewrite-url-rewriting-explained is the slug, /questions/20563772/reference-mod-rewrite-url-rewriting-explained is the pretty URL.

    – deceze
    Dec 13 '13 at 12:54






  • 1





    I think that the .htaccess and mod-rewrite tags should be updated to include a link to this question, as it covers much of what is asked on a regular basis. Thoughts?

    – Mike Rockétt
    Feb 13 '16 at 13:23













131












131








131


85






"Pretty links" is an often requested topic, but it is rarely fully explained. mod_rewrite is one way to make "pretty links", but it's complex and its syntax is very terse, hard to grok and the documentation assumes a certain level of proficiency in HTTP. Can someone explain in simple terms how "pretty links" work and how mod_rewrite can be used to create them?



Other common names, aliases, terms for clean urls: RESTful URLs, User-friendly URLs, SEO-friendly URLs, Slugging, MVC urls (probably a misnomer)










share|improve this question
















"Pretty links" is an often requested topic, but it is rarely fully explained. mod_rewrite is one way to make "pretty links", but it's complex and its syntax is very terse, hard to grok and the documentation assumes a certain level of proficiency in HTTP. Can someone explain in simple terms how "pretty links" work and how mod_rewrite can be used to create them?



Other common names, aliases, terms for clean urls: RESTful URLs, User-friendly URLs, SEO-friendly URLs, Slugging, MVC urls (probably a misnomer)







apache .htaccess mod-rewrite friendly-url






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 22 '17 at 16:20









Taryn

193k47295358




193k47295358










asked Dec 13 '13 at 10:07









decezedeceze

399k63544701




399k63544701







  • 1





    Slug or Slugging is another common alias/term for pretty urls.

    – Mike B
    Dec 13 '13 at 12:30







  • 2





    @Mike Sort of, but slugs are often a part of pretty URLs. A slug is pretty specifically when, for example, the headline of an article is turned into a URL-friendly form which then acts as the identifier of that article. So reference-mod-rewrite-url-rewriting-explained is the slug, /questions/20563772/reference-mod-rewrite-url-rewriting-explained is the pretty URL.

    – deceze
    Dec 13 '13 at 12:54






  • 1





    I think that the .htaccess and mod-rewrite tags should be updated to include a link to this question, as it covers much of what is asked on a regular basis. Thoughts?

    – Mike Rockétt
    Feb 13 '16 at 13:23












  • 1





    Slug or Slugging is another common alias/term for pretty urls.

    – Mike B
    Dec 13 '13 at 12:30







  • 2





    @Mike Sort of, but slugs are often a part of pretty URLs. A slug is pretty specifically when, for example, the headline of an article is turned into a URL-friendly form which then acts as the identifier of that article. So reference-mod-rewrite-url-rewriting-explained is the slug, /questions/20563772/reference-mod-rewrite-url-rewriting-explained is the pretty URL.

    – deceze
    Dec 13 '13 at 12:54






  • 1





    I think that the .htaccess and mod-rewrite tags should be updated to include a link to this question, as it covers much of what is asked on a regular basis. Thoughts?

    – Mike Rockétt
    Feb 13 '16 at 13:23







1




1





Slug or Slugging is another common alias/term for pretty urls.

– Mike B
Dec 13 '13 at 12:30






Slug or Slugging is another common alias/term for pretty urls.

– Mike B
Dec 13 '13 at 12:30





2




2





@Mike Sort of, but slugs are often a part of pretty URLs. A slug is pretty specifically when, for example, the headline of an article is turned into a URL-friendly form which then acts as the identifier of that article. So reference-mod-rewrite-url-rewriting-explained is the slug, /questions/20563772/reference-mod-rewrite-url-rewriting-explained is the pretty URL.

– deceze
Dec 13 '13 at 12:54





@Mike Sort of, but slugs are often a part of pretty URLs. A slug is pretty specifically when, for example, the headline of an article is turned into a URL-friendly form which then acts as the identifier of that article. So reference-mod-rewrite-url-rewriting-explained is the slug, /questions/20563772/reference-mod-rewrite-url-rewriting-explained is the pretty URL.

– deceze
Dec 13 '13 at 12:54




1




1





I think that the .htaccess and mod-rewrite tags should be updated to include a link to this question, as it covers much of what is asked on a regular basis. Thoughts?

– Mike Rockétt
Feb 13 '16 at 13:23





I think that the .htaccess and mod-rewrite tags should be updated to include a link to this question, as it covers much of what is asked on a regular basis. Thoughts?

– Mike Rockétt
Feb 13 '16 at 13:23












4 Answers
4






active

oldest

votes


















95














To understand what mod_rewrite does you first need to understand how a web server works. A web server responds to HTTP requests. An HTTP request at its most basic level looks like this:



GET /foo/bar.html HTTP/1.1


This is the simple request of a browser to a web server requesting the URL /foo/bar.html from it. It is important to stress that it does not request a file, it requests just some arbitrary URL. The request may also look like this:



GET /foo/bar?baz=42 HTTP/1.1


This is just as valid a request for a URL, and it has more obviously nothing to do with files.



The web server is an application listening on a port, accepting HTTP requests coming in on that port and returning a response. A web server is entirely free to respond to any request in any way it sees fit/in any way you have configured it to respond. This response is not a file, it's an HTTP response which may or may not have anything to do with physical files on any disk. A web server doesn't have to be Apache, there are many other web servers which are all just programs which run persistently and are attached to a port which respond to HTTP requests. You can write one yourself. This paragraph was intended to divorce you from any notion that URLs directly equal files, which is really important to understand. :)



The default configuration of most web servers is to look for a file that matches the URL on the hard disk. If the document root of the server is set to, say, /var/www, it may look whether the file /var/www/foo/bar.html exists and serve it if so. If the file ends in ".php" it will invoke the PHP interpreter and then return the result. All this association is completely configurable; a file doesn't have to end in ".php" for the web server to run it through the PHP interpreter, and the URL doesn't have to match any particular file on disk for something to happen.



mod_rewrite is a way to rewrite the internal request handling. When the web server receives a request for the URL /foo/bar, you can rewrite that URL into something else before the web server will look for a file on disk to match it. Simple example:



RewriteEngine On
RewriteRule /foo/bar /foo/baz


This rule says whenever a request matches "/foo/bar", rewrite it to "/foo/baz". The request will then be handled as if /foo/baz had been requested instead. This can be used for various effects, for example:



RewriteRule (.*) $1.html


This rule matches anything (.*) and captures it ((..)), then rewrites it to append ".html". In other words, if /foo/bar was the requested URL, it will be handled as if /foo/bar.html had been requested. See http://regular-expressions.info for more information about regular expression matching, capturing and replacements.



Another often encountered rule is this:



RewriteRule (.*) index.php?url=$1


This, again, matches anything and rewrites it to the file index.php with the originally requested URL appended in the url query parameter. I.e., for any and all requests coming in, the file index.php is executed and this file will have access to the original request in $_GET['url'], so it can do anything it wants with it.



Primarily you put these rewrite rules into your web server configuration file. Apache also allows* you to put them into a file called .htaccess within your document root (i.e. next to your .php files).



* If allowed by the primary Apache configuration file; it's optional, but often enabled.



What mod_rewrite does not do



mod_rewrite does not magically make all your URLs "pretty". This is a common misunderstanding. If you have this link in your web site:



<a href="/my/ugly/link.php?is=not&amp;very=pretty">


there's nothing mod_rewrite can do to make that pretty. In order to make this a pretty link, you have to:




  1. Change the link to a pretty link:



    <a href="/my/pretty/link">


  2. Use mod_rewrite on the server to handle the request to the URL /my/pretty/link using any one of the methods described above.


(One could use mod_substitute in conjunction to transform outgoing HTML pages and their contained links. Though this is usally more effort than just updating your HTML resources.)



There's a lot mod_rewrite can do and very complex matching rules you can create, including chaining several rewrites, proxying requests to a completely different service or machine, returning specific HTTP status codes as responses, redirecting requests etc. It's very powerful and can be used to great good if you understand the fundamental HTTP request-response mechanism. It does not automatically make your links pretty.



See the official documentation for all the possible flags and options.






share|improve this answer




















  • 5





    Maybe mention the FallbackResource directive introduced in version 2.2.16 as the preffered way of rewriting to a dispatcher.

    – Darsstar
    Dec 13 '13 at 10:31



















72














To expand on deceze's answer, I wanted to provide a few examples and explanation of some other mod_rewrite functionality.



All of the below examples assume that you have already included RewriteEngine On in your .htaccess file.



Rewrite Example



Lets take this example:



RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]


The rule is split into 4 sections:




  1. RewriteRule - starts the rewrite rule


  2. ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ - This is called the pattern, however I'll just refer to it as the left hand side of the rule - what you want to rewrite from


  3. blog/index.php?id=$1&title=$2 - called the substitution, or right hand side of a rewrite rule - what you want to rewrite to


  4. [NC,L,QSA] are flags for the rewrite rule, separated by a comma, which I will explain more on later

The above rewrite would allow you to link to something like /blog/1/foo/ and it would actually load /blog/index.php?id=1&title=foo.



Left hand side of the rule




  • ^ indicates the start of the page name - so it will rewrite example.com/blog/... but not example.com/foo/blog/...

  • Each set of (…) parentheses represents a regular expression that we can capture as a variable in the right hand side of the rule. In this example:

    • The first set of brackets - ([0-9]+) - matches a string with a minimum of 1 character in length and with only numeric values (i.e. 0-9). This can be referenced with $1 in the right hand side of the rule

    • The second set of parentheses matches a string with a minimum of 1 character in length, containing only alphanumeric characters (A-Z, a-z, or 0-9) or - or + (note + is escaped with a backslash as without escaping it this will execute as a regex repetition character). This can be referenced with $2 in the right hand side of the rule



  • ? means that the preceding character is optional, so in this case both /blog/1/foo/ and /blog/1/foo would rewrite to the same place


  • $ indicates this is the end of the string we want to match

Flags



These are options that are added in square brackets at the end of your rewrite rule to specify certain conditions. Again, there are a lot of different flags which you can read up on in the documentation, but I'll go through some of the more common flags:



NC


The no case flag means that the rewrite rule is case insensitive, so for the example rule above this would mean that both /blog/1/foo/ and /BLOG/1/foo/ (or any variation of this) would be matched.



L


The last flag indicates that this is the last rule that should be processed. This means that if and only if this rule matches, no further rules will be evaluated in the current rewrite processing run. If the rule does not match, all other rules will be tried in order as usual. If you do not set the L flag, all following rules will be applied to the rewritten URL afterwards.



END


Since Apache 2.4 you can also use the [END] flag. A matching rule with it will completely terminate further alias/rewrite processing. (Whereas the [L] flag can oftentimes trigger a second round, for example when rewriting into or out of subdirectories.)



QSA


The query string append flag allows us to pass in extra variables to the specified URL which will get added to the original get parameters. For our example this means that something like /blog/1/foo/?comments=15 would load /blog/index.php?id=1&title=foo&comments=15



R


This flag isn't one I used in the example above, but is one I thought is worth mentioning. This allows you to specify a http redirect, with the option to include a status code (e.g. R=301). For example if you wanted to do a 301 redirect on /myblog/ to /blog/ you would simply write a rule something like this:



RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]


Rewrite Conditions



Rewrite conditions make rewrites even more powerful, allowing you to specify rewrites for more specific situations. There are a lot of conditions which you can read about in the documentation, but I'll touch on a few common examples and explain them:



# if the host doesn't start with www. then add it and redirect
RewriteCond %HTTP_HOST !^www.
RewriteRule ^ http://www.%HTTP_HOST%REQUEST_URI [L,R=301]


This is a very common practice, which will prepend your domain with www. (if it isn't there already) and execute a 301 redirect. For example, loading up http://example.com/blog/ it would redirect you to http://www.example.com/blog/



# if it cant find the image, try find the image on another domain
RewriteCond %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC]
RewriteCond %REQUEST_FILENAME !-f
RewriteCond %REQUEST_FILENAME !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]


This is slightly less common, but is a good example of a rule that doesn't execute if the filename is a directory or file that exists on the server.




  • %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC] will only execute the rewrite for files with a file extension of jpg, jpeg, gif or png (case insensitive).


  • %REQUEST_FILENAME !-f will check to see if the file exists on the current server, and only execute the rewrite if it doesn't


  • %REQUEST_FILENAME !-d will check to see if the file exists on the current server, and only execute the rewrite if it doesn't

  • The rewrite will attempt to load the same file on another domain





share|improve this answer
































    36














    References



    Stack Overflow has many other great resources to get started:




    • Serverfault: Everything you ever wanted to know about mod_rewrite
      (Keep in mind to remove the slash in ^/ pattern prefixes for .htaccess usage.)

    • Do's and Dont's in Hidden features of mod_rewrite.

    • Look through our most popular mod-rewrite questions and answers.

    • Apache redirecting and remapping guide.

    • AskApache ultimate .htaccess guide

    • And the mod-rewrite tag wiki references.

    And newcomer-friendly regex overviews even:



    • Our regex tag wiki for a syntax compendium.

    • And the short Apache regex summary.

    • Else regexp.info for easy-to-understand basics.

    Oft-used placeholders




    • .* matches anything, even an empty string. You don't want to use this pattern everywhere, but often in the last fallback rule.


    • [^/]+ is more often used for path segments. It matches anything but the forward slash.


    • d+ only matches numeric strings.


    • w+ matches alphanumeric characters. It's basically shorthand for [A-Za-z0-9_].


    • [w-]+ for "slug"-style path segments, using letters, numbers, dash - and _


    • [w-.,]+ adds periods and commas. Prefer an escaped - dash in […] charclasses.


    • . denotes a literal period. Otherwise . outside of […] is placeholder for any symbol.

    Each of these placeholders is usually wrapped in (…) parentheses as capture group. And the whole pattern often in ^………$ start + end markers. Quoting "patterns" is optional.



    RewriteRules



    The following examples are PHP-centric and a bit more incremental, easier to adapt for similar cases.
    They're just summaries, often link to more variations or detailed Q&As.






    • Static mapping
      /contact, /about



      Shortening a few page names to internal file schemes is most simple:



       RewriteRule ^contact$ templ/contact.html
      RewriteRule ^about$ about.php



    • Numeric identifiers
      /object/123



      Introducing shortcuts like http://example.com/article/531 to existing PHP scripts is also easy. The numeric placeholder can just be remapped to a $_GET parameter:



       RewriteRule ^article/(d+)$ article-show.php?id=$1
      # └───────────────────────────┘



    • Slug-style placeholders
      /article/with-some-title-slug



      You can easily extend that rule to allow for /article/title-string placeholders:



       RewriteRule ^article/([w-]+)$ article-show.php?title=$1
      # └────────────────────────────────┘


      Note that your script must be able (or be adapted) to map those titles back to database-ids. RewriteRules alone can't create or guess information out of thin air.




    • Slugs with numeric prefixes
      /readable/123-plus-title



      Therefore you'll often see mixed /article/529-title-slug paths used in practice:



       RewriteRule ^article/(d+)-([w-]+)$ article.php?id=$1&title=$2
      # └───────────────────────────────┘


      Now you could just skip passing the title=$2 anyway, because your script will typically rely on the database-id anyway. The -title-slug has become arbitrary URL decoration.




    • Uniformity with alternative lists
      /foo/… /bar/… /baz/…



      If you have similar rules for multiple virtual page paths, then you can match and compact them with | alternative lists. And again just reassign them to internal GET parameters:



       # ┌─────────────────────────┐
      RewriteRule ^(blog|post|user)/(w+)$ disp.php?type=$1&id=$2
      # └───────────────────────────────────┘


      You can split them out into individual RewriteRules should this get too complex.




    • Dispatching related URLs to different backends
      /date/SWITCH/backend



      A more practical use of alternative lists are mapping request paths to distinct scripts. For example to provide uniform URLs for an older and a newer web application based on dates:



       # ┌─────────────────────────────┐
      # │ ┌───────────┼───────────────┐
      RewriteRule ^blog/(2009|2010|2011)/([d-]+)/?$ old/blog.php?date=$2
      RewriteRule ^blog/(d+)/([d-]+)/?$ modern/blog/index.php?start=$2
      # └──────────────────────────────────────┘


      This simply remaps 2009-2011 posts onto one script, and all other years implicitly to another handler.
      Note the more specific rule coming first. Each script might use different GET params.




    • Other delimiters than just / path slashes
      /user-123-name



      You're most commonly seeing RewriteRules to simulate a virtual directory structure. But you're not forced to be uncreative. You can as well use - hyphens for segmenting or structure.



       RewriteRule ^user-(d+)$ show.php?what=user&id=$1
      # └──────────────────────────────┘
      # This could use `(w+)` alternatively for user names instead of ids.


      For the also common /wiki:section:Page_Name scheme:



       RewriteRule ^wiki:(w+):(w+)$ wiki.php?sect=$1&page=$2 
      # └─────┼────────────────────┘ │
      # └────────────────────────────┘


      Occasionally it's suitable to alternate between /-delimiters and : or . in the same rule even. Or have two RewriteRules again to map variants onto different scripts.




    • Optional trailing / slash
      /dir = /dir/



      When opting for directory-style paths, you can make it reachable with and without a final /



       RewriteRule ^blog/([w-]+)/?$ blog/show.php?id=$1
      # ┗┛


      Now this handles both http://example.com/blog/123 and /blog/123/. And the /?$ approach is easy to append onto any other RewriteRule.




    • Flexible segments for virtual paths
      .*/.*/.*/.*



      Most rules you'll encounter map a constrained set of /…/ resource path segments to individual GET parameters. Some scripts handle a variable number of options however.
      The Apache regexp engine doesn't allow optionalizing an arbitrary number of them. But you can easily expand it into a rule block yourself:



       Rewriterule ^(w+)/?$ in.php?a=$1
      Rewriterule ^(w+)/(w+)/?$ in.php?a=$1&b=$2
      Rewriterule ^(w+)/(w+)/(w+)/?$ in.php?a=$1&b=$2&c=$3
      # └─────┴─────┴───────────────────┴────┴────┘


      If you need up to five path segments, then copy this scheme along into five rules. You can of course use a more specific [^/]+ placeholder each.
      Here the ordering isn't as important, as neither overlaps. So having the most frequently used paths first is okay.



      Alternatively you can utilize PHPs array parameters via ?p=$1&p=$2&p=3 query string here - if your script merely prefers them pre-split.
      (Though it's more common to just use a catch-all rule, and let the script itself expand the segments out of the REQUEST_URI.)



      See also: How do I transform my URL path segments into query string key-value pairs?




    • Optional segments
      prefix/opt?/.*



      A common variation is to have optional prefixes within a rule. This usually makes sense if you have static strings or more constrained placeholders around:



       RewriteRule ^(w+)(?:/([^/]+))?/(w+)$ ?main=$1&opt=$2&suffix=$3


      Now the more complex pattern (?:/([^/])+)? there simply wraps a non-capturing (?:…) group, and makes it optional )?. The contained
      placeholder ([^/]+) would be substitution pattern $2, but be empty if there's no middle /…/ path.




    • Capture the remainder
      /prefix/123-capture/…/*/…whatever…



      As said before, you don't often want too generic rewrite patterns. It does however make sense to combine static and specific comparisons with a .* sometimes.



       RewriteRule ^(specific)/prefix/(d+)(/.*)?$ speci.php?id=$2&otherparams=$2


      This optionalized any /…/…/… trailing path segments. Which then of course requires the handling script to split them up, and variabl-ify extracted parameters
      itself (which is what Web-"MVC" frameworks do).




    • Trailing file "extensions"
      /old/path.HTML



      URLs don't really have file extensions. Which is what this entire reference is about (= URLs are virtual locators, not necessarily a direct filesystem image).
      However if you had a 1:1 file mapping before, you can craft simpler rules:



       RewriteRule ^styles/([w.-]+).css$ sass-cache.php?old_fn_base=$1
      RewriteRule ^images/([w.-]+).gif$ png-converter.php?load_from=$2


      Other common uses are remapping obsolete .html paths to newer .php handlers, or just aliasing directory names only for individual (actual/real) files.




    • Ping-Pong (redirects and rewrites in unison)
      /ugly.html ←→ /pretty



      So at some point you're rewriting your HTML pages to carry only pretty links, as outlined by deceze.
      Meanwhile you'll still receive requests for the old paths, sometimes even from bookmarks. As workaround, you can ping-pong browsers to display/establish
      the new URLs.



      This common trick involves sending a 30x/Location redirect whenever an incoming URL follows the obsolete/ugly naming scheme.
      Browsers will then rerequest the new/pretty URL, which afterwards is rewritten (just internally) to the original or new location.



       # redirect browser for old/ugly incoming paths
      RewriteRule ^old/teams.html$ /teams [R=301,QSA,END]

      # internally remap already-pretty incoming request
      RewriteRule ^teams$ teams.php [QSA,END]


      Note how this example just uses [END] instead of [L] to safely alternate. For older Apache 2.2 versions you can use other workarounds, besides also remapping
      query string parameters for example:
      Redirect ugly to pretty URL, remap back to the ugly path, without infinite loops




    • Spaces in patterns
      /this+that+



      It's not that pretty in browser address bars, but you can use spaces in URLs. For rewrite patterns use backslash-escaped spaces.
      Else just "-quote the whole pattern or substitution:



       RewriteRule "^this [w ]+/(.*)$" "index.php?id=$1" [L]


      Clients serialize URLs with + or %20 for spaces. Yet in RewriteRules they're interpreted with literal characters for all relative path segments.



    Frequent duplicates:




    • Catch-all for a central dispatcher / front-controller script



       RewriteCond %REQUEST_URI !-f
      RewriteCond %REQUEST_URI !-d
      RewriteRule ^.*$ index.php [L]


      Which is often used by PHP frameworks or WebCMS / portal scripts. The actual path splitting then is handled in PHP using $_SERVER["REQUEST_URI"]. So conceptionally it's pretty much the opposite of URL handling "per mod_rewrite". (Just use FallBackResource instead.)




    • Remove www. from hostname



      Note that this doesn't copy a query string along, etc.



       # ┌──────────┐
      RewriteCond %HTTP_HOST ^www.(.+)$ [NC] │
      RewriteRule ^(.*)$ http://%1/$1 [R=301,L] │
      # ↓ └───┼────────────┘
      # └───────────────┘


      See also:

      · URL rewriting for different protocols in .htaccess

      · Generic htaccess redirect www to non-www

      · .htaccess - how to force "www." in a generic way?



      Note that RewriteCond/RewriteRule combos can be more complex, with matches (%1 and $1) interacting in both directions even:



      References %1 and $2, %3 between RewriteRule and RewriteCond
      Apache manual - mod_rewrite intro, Copyright 2015 The Apache Software Foundation, AL-2.0




    • Redirect to HTTPS://



       RewriteCond %SERVER_PORT 80
      RewriteRule ^(.*)$ https://example.com/$1 [R,L]


      See also: https://wiki.apache.org/httpd/RewriteHTTPToHTTPS




    • "Removing" the PHP extension



       RewriteCond %REQUEST_FILENAME.php -f
      RewriteRule ^(.+)$ $1.php [L] # or [END]


      See also: Removing the .php extension with mod_rewrite




    • Aliasing old .html paths to .php scripts



      See: http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility




    • Rewrite from URL like "/page" to a script such as "/index.php/page"



      See mod_rewrite, php and the .htaccess file




    • Redirect subdomain to a folder



      See How can i get my htaccess to work (subdomains)?



    Prevalent .htaccess pitfalls



    Now take this with a grain of salt. Not every advise can be generalized to all contexts.
    This is just a simple summary of well-known and a few unobvious stumbling blocks:




    • Enable mod_rewrite and .htaccess



      To actually use RewriteRules in per-directory configuration files you must:



      • Check that your server has AllowOverride All enabled. Otherwise your per-directory .htaccess directives will go ignored, and RewriteRules won't work.


      • Obviously have mod_rewrite enabled in your httpd.conf modules section.


      • Prepend each list of rules with RewriteEngine On still. While mod_rewrite is implicitly active in <VirtualHost> and <Directory> sections,
        the per-directory .htaccess files need it individually summoned.




    • The leading slash ^/ won't match



      You shouldn't start your .htaccess RewriteRule patterns with ^/ normally:



       RewriteRule ^/article/d+$ …



      This is often seen in old tutorials. And it used to be correct for ancient Apache 1.x versions. Nowadays request paths are conveniently fully directory-relative in .htaccess RewriteRules. Just leave the leading / out.



      · Note that the leading slash is still correct in <VirtualHost> sections though. Which is why you often see it ^/? optionalized for rule parity.

      · Or when using a RewriteCond %REQUEST_URI you'd still match for a leading /.

      · See also Webmaster.SE: When is the leading slash (/) needed in mod_rewrite patterns?





    • <IfModule *> wrappers begone!



      You've probably seen this in many examples:



      <IfModule mod_rewrite.c>
      Rewrite…
      </IfModule>


      • It does make sense in <VirtualHost> sections - if it was combined with another fallback option, such as ScriptAliasMatch. (But nobody ever does that).

      • And it's commonly distributed for default .htaccess rulesets with many open source projects. There it's just meant as fallback, and keeps "ugly" URLs work as default.

      However you don't want that usually in your own .htaccess files.



      • Firstly, mod_rewrite does not randomly disengage. (If it did, you'd have bigger problems).

      • Were it really be disabled, your RewriteRules still wouldn't work anyway.

      • It's meant to prevent HTTP 500 errors. What it usually accomplishes is gracing your users with HTTP 404 errors instead. (Not so much more user-friendly if you think about it.)

      • Practically it just suppresses the more useful log entries, or server notification mails. You'd be none the wiser as to why your RewriteRules never work.


      What seems enticing as generalized safeguard, often turns out to be an obstacle in practice.





    • Don't use RewriteBase unless needed



      Many copy+paste examples contain a RewriteBase / directive. Which happens to be the implicit default anyway. So you don't actually need this. It's a workaround for fancy VirtualHost rewriting schemes, and misguessed DOCUMENT_ROOT paths for some shared hosters.



      It makes sense to use with individual web applications in deeper subdirectories. It can shorten RewriteRule patterns in such cases. Generally it's best to prefer relative path specifiers in per-directory rule sets.



      See also How does RewriteBase work in .htaccess




    • Disable MultiViews when virtual paths overlap



      URL rewriting is primarily used for supporting virtual incoming paths. Commonly you just have one dispatcher script (index.php) or a few individual handlers (articles.php, blog.php, wiki.php, …). The latter might clash with similar virtual RewriteRule paths.



      A request for /article/123 for example could map to article.php with a /123 PATH_INFO implicitly. You'd either have to guard your rules then with the commonplace RewriteCond !-f+!-d, and/or disable PATH_INFO support, or perhaps just disable Options -MultiViews.



      Which is not to say you always have to. Content-Negotiation is just an automatism to virtual resources.




    • Ordering is important



      See Everything you ever wanted to know about mod_rewrite
      if you haven't already. Combining multiple RewriteRules often leads to interaction. This isn't something to prevent habitually per [L] flag, but a scheme you'll embrace once versed.
      You can re-re-rewrite virtual paths from one rule to another, until it reaches an actual target handler.



      Still you'd often want to have the most specific rules (fixed string /forum/… patterns, or more restrictive placeholders [^/.]+) in the early rules.
      Generic slurp-all rules (.*) are better left to the later ones. (An exception is a RewriteCond -f/-d guard as primary block.)




    • Stylesheets and images stop working



      When you introduce virtual directory structures /blog/article/123 this impacts relative resource references in HTML (such as <img src=mouse.png>).
      Which can be solved by:



      • Only using server-absolute references href="/old.html" or src="/logo.png"

      • Often simply by adding <base href="/index"> into your HTML <head> section.
        This implicitly rebinds relative references to what they were before.

      You could alternatively craft further RewriteRules to rebind .css or .png paths to their original locations.
      But that's both unneeded, or incurs extra redirects and hampers caching.



      See also: CSS, JS and images do not display with pretty url




    • RewriteConds just mask one RewriteRule



      A common misinterpetation is that a RewriteCond blocks multiple RewriteRules (because they're visually arranged together):



       RewriteCond %SERVER_NAME localhost
      RewriteRule ^secret admin/tools.php
      RewriteRule ^hidden sqladmin.cgi


      Which it doesn't per default. You can chain them using the [S=2] flag. Else you'll have to repeat them. While sometimes you can craft an "inverted" primary rule to [END] the rewrite processing early.




    • QUERY_STRING exempt from RewriteRules



      You can't match RewriteRule index.php?x=y, because mod_rewrite compares just against relative paths per default. You can match them separately however via:



       RewriteCond %QUERY_STRING b(?:param)=([^&]+)(?:&|$)
      RewriteRule ^add/(.+)$ add/%1/$1 # ←──﹪₁──┘


      See also How can I match query string variables with mod_rewrite?





    • .htaccess vs. <VirtualHost>



      If you're using RewriteRules in a per-directory config file, then worrying about regex performance is pointless. Apache retains
      compiled PCRE patterns longer than a PHP process with a common routing framework. For high-traffic sites you should however consider
      moving rulesets into the vhost server configuration, once they've been battle-tested.



      In this case, prefer the optionalized ^/? directory separator prefix. This allows to move RewriteRules freely between PerDir and server
      config files.




    • Whenever something doesn't work



      Fret not.




      • Compare access.log and error.log



        Often you can figure out how a RewriteRule misbehaves just from looking at your error.log and access.log.
        Correlate access times to see which request path originally came in, and which path/file Apache couldn't resolve to (error 404/500).



        This doesn't tell you which RewriteRule is the culprit. But inaccessible final paths like /docroot/21-.itle?index.php may give away where to inspect further.
        Otherwise disable rules until you get some predictable paths.




      • Enable the RewriteLog



        See Apache RewriteLog docs. For debugging you can enable it in the vhost sections:



        # Apache 2.2
        RewriteLogLevel 5
        RewriteLog /tmp/rewrite.log

        # Apache 2.4
        LogLevel alert rewrite:trace5
        #ErrorLog /tmp/rewrite.log


        That yields a detailed summary of how incoming request paths get modified by each rule:



        [..] applying pattern '^test_.*$' to uri 'index.php'
        [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
        [..] applying pattern '^index.php$' to uri 'index.php'


        Which helps to narrow down overly generic rules and regex mishaps.



        See also:

        · .htaccess not working (mod_rewrite)

        · Tips for debugging .htaccess rewrite rules




      • Before asking your own question



        As you might know, Stack Overflow is very suitable for asking questions on mod_rewrite. Make them on-topic
        by including prior research and attempts (avoid redundant answers), demonstrate basic regex understanding, and:



        • Include full examples of input URLs, falsly rewritten target paths, your real directory structure.

        • The complete RewriteRule set, but also single out the presumed defective one.

        • Apache and PHP versions, OS type, filesystem, DOCUMENT_ROOT, and PHPs $_SERVER environment if it's about a parameter mismatch.

        • An excerpt from your access.log and error.log to verify what the existing rules resolved to. Better yet, a rewrite.log summary.

        This nets quicker and more exact answers, and makes them more useful to others.





    • Comment your .htaccess



      If you copy examples from somewhere, take care to include a # comment and origin link. While it's merely bad manners to omit attribution,
      it often really hurts maintenance later. Document any code or tutorial source. In particular while unversed you should be
      all the more interested in not treating them like magic blackboxes.




    • It's not "SEO"-URLs



      Disclaimer: Just a pet peeve. You often hear pretty URL rewriting schemes referred to as "SEO" links or something. While this is useful for googling examples, it's a dated misnomer.



      None of the modern search engines are really disturbed by .html and .php in path segments, or ?id=123 query strings for that matter. Search engines of old, such as AltaVista, did avoid crawling websites with potentially ambigious access paths. Modern crawlers are often even craving for deep web resources.



      What "pretty" URLs should conceptionally be used for is making websites user-friendly.



      1. Having readable and obvious resource schemes.

      2. Ensuring URLs are long-lived (AKA permalinks).

      3. Providing discoverability through /common/tree/nesting.

      However don't sacrifice unique requirements for conformism.



    Tools



    There are various online tools to generate RewriteRules for most GET-parameterish URLs:



    • http://www.generateit.net/mod-rewrite/index.php

    • http://www.ipdistance.com/mod_rewrite.php

    • http://webtools.live2support.com/misc_rewrite.php

    Mostly just output [^/]+ generic placeholders, but likely suffices for trivial sites.






    share|improve this answer

























    • Still needs a bit rewriting, more links, and the many subheadlines are somewhat obnoxious. There's some overlap with the other answers here, so can be cut down perhaps. It's mainly about the visual examples though, and that list of common gotchas.

      – mario
      Jul 7 '15 at 22:07






    • 2





      Didn't saw such a beauty of an answer for a long time! My eyes are glowing while I'm reading it. Please don't stop posting such answers :)

      – Rizier123
      Jul 8 '15 at 18:56











    • Excellent post. Made me understand the basic concepts of mod_rewrite very quickly!

      – breez
      Dec 9 '16 at 11:20


















    5














    Alternatives to mod_rewrite



    Many basic virtual URL schemes can be achieved without using RewriteRules. Apache allows PHP scripts to be invoked without .php extension, and with a virtual PATH_INFO argument.




    1. Use the PATH_INFO, Luke



      Nowadays AcceptPathInfo On is often enabled by default. Which basically allows .php and other resource URLs to carry a virtual argument:



      http://example.com/script.php/virtual/path


      Now this /virtual/path shows up in PHP as $_SERVER["PATH_INFO"] where you can handle any extra arguments however you like.



      This isn't as convenient as having Apache separate input path segments into $1, $2, $3 and passing them as distinct $_GET variables to PHP. It's merely emulating "pretty URLs" with less configuration effort.




    2. Enable MultiViews to hide the .php extension



      The simplest option to also eschew .php "file extensions" in URLs is enabling:



      Options +MultiViews


      This has Apache select article.php for HTTP requests on /article due to the matching basename. And this works well together with the aforementioned PATH_INFO feature. So you can just use URLs like http://example.com/article/virtual/title. Which makes sense if you have a traditional web application with multiple PHP invocation points/scripts.



      Note that MultiViews has a different/broader purpose though. It incurs a very minor performance penalty, because Apache always looks for other files with matching basenames. It's actually meant for Content-Negotiation, so browsers receive the best alternative among available resources (such as article.en.php, article.fr.php, article.jp.mp4).




    3. SetType or SetHandler for extensionless .php scripts



      A more directed approach to avoid carrying around .php suffixes in URLs is configuring the PHP handler for other file schemes. The simplest option is overriding the default MIME/handler type via .htaccess:



      DefaultType application/x-httpd-php


      This way you could just rename your article.php script to just article (without extension), but still have it processed as PHP script.



      Now this can have some security and performance implications, because all extensionless files would be piped through PHP now. Therefore you can alternatively set this behaviour for individual files only:



      <Files article>
      SetHandler application/x-httpd-php
      # or SetType
      </Files>


      This is somewhat dependent on your server setup and the used PHP SAPI. Common alternatives include ForceType application/x-httpd-php or AddHandler php5-script.




      Again take note that such settings propagate from one .htaccess to subfolders. You always should disable script execution (SetHandler None and Options -Exec or php_flag engine off etc.) for static resources, and upload/ directories etc.





    4. Other Apache rewriting schemes



      Among its many options, Apache provides mod_alias features - which sometimes work just as well as mod_rewrites RewriteRules. Note that most of those must be set up in a <VirtualHost> section however, not in per-directory .htaccess config files.



      • ScriptAliasMatch is primarily for CGI scripts, but also ought to works for PHP. It allows regexps just like any RewriteRule. In fact it's perhaps the most robust option to configurate a catch-all front controller.


      • And a plain Alias helps with a few simple rewriting schemes as well.


      • Even a plain ErrorDocument directive could be used to let a PHP script handle virtual paths. Note that this is a kludgy workaround however, prohibits anything but GET requests, and floods the error.log by definition.


      See http://httpd.apache.org/docs/2.2/urlmapping.html for further tips.







    share|improve this answer























      protected by hjpotter92 Nov 17 '14 at 23:32



      Thank you for your interest in this question.
      Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).



      Would you like to answer one of these unanswered questions instead?














      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      95














      To understand what mod_rewrite does you first need to understand how a web server works. A web server responds to HTTP requests. An HTTP request at its most basic level looks like this:



      GET /foo/bar.html HTTP/1.1


      This is the simple request of a browser to a web server requesting the URL /foo/bar.html from it. It is important to stress that it does not request a file, it requests just some arbitrary URL. The request may also look like this:



      GET /foo/bar?baz=42 HTTP/1.1


      This is just as valid a request for a URL, and it has more obviously nothing to do with files.



      The web server is an application listening on a port, accepting HTTP requests coming in on that port and returning a response. A web server is entirely free to respond to any request in any way it sees fit/in any way you have configured it to respond. This response is not a file, it's an HTTP response which may or may not have anything to do with physical files on any disk. A web server doesn't have to be Apache, there are many other web servers which are all just programs which run persistently and are attached to a port which respond to HTTP requests. You can write one yourself. This paragraph was intended to divorce you from any notion that URLs directly equal files, which is really important to understand. :)



      The default configuration of most web servers is to look for a file that matches the URL on the hard disk. If the document root of the server is set to, say, /var/www, it may look whether the file /var/www/foo/bar.html exists and serve it if so. If the file ends in ".php" it will invoke the PHP interpreter and then return the result. All this association is completely configurable; a file doesn't have to end in ".php" for the web server to run it through the PHP interpreter, and the URL doesn't have to match any particular file on disk for something to happen.



      mod_rewrite is a way to rewrite the internal request handling. When the web server receives a request for the URL /foo/bar, you can rewrite that URL into something else before the web server will look for a file on disk to match it. Simple example:



      RewriteEngine On
      RewriteRule /foo/bar /foo/baz


      This rule says whenever a request matches "/foo/bar", rewrite it to "/foo/baz". The request will then be handled as if /foo/baz had been requested instead. This can be used for various effects, for example:



      RewriteRule (.*) $1.html


      This rule matches anything (.*) and captures it ((..)), then rewrites it to append ".html". In other words, if /foo/bar was the requested URL, it will be handled as if /foo/bar.html had been requested. See http://regular-expressions.info for more information about regular expression matching, capturing and replacements.



      Another often encountered rule is this:



      RewriteRule (.*) index.php?url=$1


      This, again, matches anything and rewrites it to the file index.php with the originally requested URL appended in the url query parameter. I.e., for any and all requests coming in, the file index.php is executed and this file will have access to the original request in $_GET['url'], so it can do anything it wants with it.



      Primarily you put these rewrite rules into your web server configuration file. Apache also allows* you to put them into a file called .htaccess within your document root (i.e. next to your .php files).



      * If allowed by the primary Apache configuration file; it's optional, but often enabled.



      What mod_rewrite does not do



      mod_rewrite does not magically make all your URLs "pretty". This is a common misunderstanding. If you have this link in your web site:



      <a href="/my/ugly/link.php?is=not&amp;very=pretty">


      there's nothing mod_rewrite can do to make that pretty. In order to make this a pretty link, you have to:




      1. Change the link to a pretty link:



        <a href="/my/pretty/link">


      2. Use mod_rewrite on the server to handle the request to the URL /my/pretty/link using any one of the methods described above.


      (One could use mod_substitute in conjunction to transform outgoing HTML pages and their contained links. Though this is usally more effort than just updating your HTML resources.)



      There's a lot mod_rewrite can do and very complex matching rules you can create, including chaining several rewrites, proxying requests to a completely different service or machine, returning specific HTTP status codes as responses, redirecting requests etc. It's very powerful and can be used to great good if you understand the fundamental HTTP request-response mechanism. It does not automatically make your links pretty.



      See the official documentation for all the possible flags and options.






      share|improve this answer




















      • 5





        Maybe mention the FallbackResource directive introduced in version 2.2.16 as the preffered way of rewriting to a dispatcher.

        – Darsstar
        Dec 13 '13 at 10:31
















      95














      To understand what mod_rewrite does you first need to understand how a web server works. A web server responds to HTTP requests. An HTTP request at its most basic level looks like this:



      GET /foo/bar.html HTTP/1.1


      This is the simple request of a browser to a web server requesting the URL /foo/bar.html from it. It is important to stress that it does not request a file, it requests just some arbitrary URL. The request may also look like this:



      GET /foo/bar?baz=42 HTTP/1.1


      This is just as valid a request for a URL, and it has more obviously nothing to do with files.



      The web server is an application listening on a port, accepting HTTP requests coming in on that port and returning a response. A web server is entirely free to respond to any request in any way it sees fit/in any way you have configured it to respond. This response is not a file, it's an HTTP response which may or may not have anything to do with physical files on any disk. A web server doesn't have to be Apache, there are many other web servers which are all just programs which run persistently and are attached to a port which respond to HTTP requests. You can write one yourself. This paragraph was intended to divorce you from any notion that URLs directly equal files, which is really important to understand. :)



      The default configuration of most web servers is to look for a file that matches the URL on the hard disk. If the document root of the server is set to, say, /var/www, it may look whether the file /var/www/foo/bar.html exists and serve it if so. If the file ends in ".php" it will invoke the PHP interpreter and then return the result. All this association is completely configurable; a file doesn't have to end in ".php" for the web server to run it through the PHP interpreter, and the URL doesn't have to match any particular file on disk for something to happen.



      mod_rewrite is a way to rewrite the internal request handling. When the web server receives a request for the URL /foo/bar, you can rewrite that URL into something else before the web server will look for a file on disk to match it. Simple example:



      RewriteEngine On
      RewriteRule /foo/bar /foo/baz


      This rule says whenever a request matches "/foo/bar", rewrite it to "/foo/baz". The request will then be handled as if /foo/baz had been requested instead. This can be used for various effects, for example:



      RewriteRule (.*) $1.html


      This rule matches anything (.*) and captures it ((..)), then rewrites it to append ".html". In other words, if /foo/bar was the requested URL, it will be handled as if /foo/bar.html had been requested. See http://regular-expressions.info for more information about regular expression matching, capturing and replacements.



      Another often encountered rule is this:



      RewriteRule (.*) index.php?url=$1


      This, again, matches anything and rewrites it to the file index.php with the originally requested URL appended in the url query parameter. I.e., for any and all requests coming in, the file index.php is executed and this file will have access to the original request in $_GET['url'], so it can do anything it wants with it.



      Primarily you put these rewrite rules into your web server configuration file. Apache also allows* you to put them into a file called .htaccess within your document root (i.e. next to your .php files).



      * If allowed by the primary Apache configuration file; it's optional, but often enabled.



      What mod_rewrite does not do



      mod_rewrite does not magically make all your URLs "pretty". This is a common misunderstanding. If you have this link in your web site:



      <a href="/my/ugly/link.php?is=not&amp;very=pretty">


      there's nothing mod_rewrite can do to make that pretty. In order to make this a pretty link, you have to:




      1. Change the link to a pretty link:



        <a href="/my/pretty/link">


      2. Use mod_rewrite on the server to handle the request to the URL /my/pretty/link using any one of the methods described above.


      (One could use mod_substitute in conjunction to transform outgoing HTML pages and their contained links. Though this is usally more effort than just updating your HTML resources.)



      There's a lot mod_rewrite can do and very complex matching rules you can create, including chaining several rewrites, proxying requests to a completely different service or machine, returning specific HTTP status codes as responses, redirecting requests etc. It's very powerful and can be used to great good if you understand the fundamental HTTP request-response mechanism. It does not automatically make your links pretty.



      See the official documentation for all the possible flags and options.






      share|improve this answer




















      • 5





        Maybe mention the FallbackResource directive introduced in version 2.2.16 as the preffered way of rewriting to a dispatcher.

        – Darsstar
        Dec 13 '13 at 10:31














      95












      95








      95







      To understand what mod_rewrite does you first need to understand how a web server works. A web server responds to HTTP requests. An HTTP request at its most basic level looks like this:



      GET /foo/bar.html HTTP/1.1


      This is the simple request of a browser to a web server requesting the URL /foo/bar.html from it. It is important to stress that it does not request a file, it requests just some arbitrary URL. The request may also look like this:



      GET /foo/bar?baz=42 HTTP/1.1


      This is just as valid a request for a URL, and it has more obviously nothing to do with files.



      The web server is an application listening on a port, accepting HTTP requests coming in on that port and returning a response. A web server is entirely free to respond to any request in any way it sees fit/in any way you have configured it to respond. This response is not a file, it's an HTTP response which may or may not have anything to do with physical files on any disk. A web server doesn't have to be Apache, there are many other web servers which are all just programs which run persistently and are attached to a port which respond to HTTP requests. You can write one yourself. This paragraph was intended to divorce you from any notion that URLs directly equal files, which is really important to understand. :)



      The default configuration of most web servers is to look for a file that matches the URL on the hard disk. If the document root of the server is set to, say, /var/www, it may look whether the file /var/www/foo/bar.html exists and serve it if so. If the file ends in ".php" it will invoke the PHP interpreter and then return the result. All this association is completely configurable; a file doesn't have to end in ".php" for the web server to run it through the PHP interpreter, and the URL doesn't have to match any particular file on disk for something to happen.



      mod_rewrite is a way to rewrite the internal request handling. When the web server receives a request for the URL /foo/bar, you can rewrite that URL into something else before the web server will look for a file on disk to match it. Simple example:



      RewriteEngine On
      RewriteRule /foo/bar /foo/baz


      This rule says whenever a request matches "/foo/bar", rewrite it to "/foo/baz". The request will then be handled as if /foo/baz had been requested instead. This can be used for various effects, for example:



      RewriteRule (.*) $1.html


      This rule matches anything (.*) and captures it ((..)), then rewrites it to append ".html". In other words, if /foo/bar was the requested URL, it will be handled as if /foo/bar.html had been requested. See http://regular-expressions.info for more information about regular expression matching, capturing and replacements.



      Another often encountered rule is this:



      RewriteRule (.*) index.php?url=$1


      This, again, matches anything and rewrites it to the file index.php with the originally requested URL appended in the url query parameter. I.e., for any and all requests coming in, the file index.php is executed and this file will have access to the original request in $_GET['url'], so it can do anything it wants with it.



      Primarily you put these rewrite rules into your web server configuration file. Apache also allows* you to put them into a file called .htaccess within your document root (i.e. next to your .php files).



      * If allowed by the primary Apache configuration file; it's optional, but often enabled.



      What mod_rewrite does not do



      mod_rewrite does not magically make all your URLs "pretty". This is a common misunderstanding. If you have this link in your web site:



      <a href="/my/ugly/link.php?is=not&amp;very=pretty">


      there's nothing mod_rewrite can do to make that pretty. In order to make this a pretty link, you have to:




      1. Change the link to a pretty link:



        <a href="/my/pretty/link">


      2. Use mod_rewrite on the server to handle the request to the URL /my/pretty/link using any one of the methods described above.


      (One could use mod_substitute in conjunction to transform outgoing HTML pages and their contained links. Though this is usally more effort than just updating your HTML resources.)



      There's a lot mod_rewrite can do and very complex matching rules you can create, including chaining several rewrites, proxying requests to a completely different service or machine, returning specific HTTP status codes as responses, redirecting requests etc. It's very powerful and can be used to great good if you understand the fundamental HTTP request-response mechanism. It does not automatically make your links pretty.



      See the official documentation for all the possible flags and options.






      share|improve this answer















      To understand what mod_rewrite does you first need to understand how a web server works. A web server responds to HTTP requests. An HTTP request at its most basic level looks like this:



      GET /foo/bar.html HTTP/1.1


      This is the simple request of a browser to a web server requesting the URL /foo/bar.html from it. It is important to stress that it does not request a file, it requests just some arbitrary URL. The request may also look like this:



      GET /foo/bar?baz=42 HTTP/1.1


      This is just as valid a request for a URL, and it has more obviously nothing to do with files.



      The web server is an application listening on a port, accepting HTTP requests coming in on that port and returning a response. A web server is entirely free to respond to any request in any way it sees fit/in any way you have configured it to respond. This response is not a file, it's an HTTP response which may or may not have anything to do with physical files on any disk. A web server doesn't have to be Apache, there are many other web servers which are all just programs which run persistently and are attached to a port which respond to HTTP requests. You can write one yourself. This paragraph was intended to divorce you from any notion that URLs directly equal files, which is really important to understand. :)



      The default configuration of most web servers is to look for a file that matches the URL on the hard disk. If the document root of the server is set to, say, /var/www, it may look whether the file /var/www/foo/bar.html exists and serve it if so. If the file ends in ".php" it will invoke the PHP interpreter and then return the result. All this association is completely configurable; a file doesn't have to end in ".php" for the web server to run it through the PHP interpreter, and the URL doesn't have to match any particular file on disk for something to happen.



      mod_rewrite is a way to rewrite the internal request handling. When the web server receives a request for the URL /foo/bar, you can rewrite that URL into something else before the web server will look for a file on disk to match it. Simple example:



      RewriteEngine On
      RewriteRule /foo/bar /foo/baz


      This rule says whenever a request matches "/foo/bar", rewrite it to "/foo/baz". The request will then be handled as if /foo/baz had been requested instead. This can be used for various effects, for example:



      RewriteRule (.*) $1.html


      This rule matches anything (.*) and captures it ((..)), then rewrites it to append ".html". In other words, if /foo/bar was the requested URL, it will be handled as if /foo/bar.html had been requested. See http://regular-expressions.info for more information about regular expression matching, capturing and replacements.



      Another often encountered rule is this:



      RewriteRule (.*) index.php?url=$1


      This, again, matches anything and rewrites it to the file index.php with the originally requested URL appended in the url query parameter. I.e., for any and all requests coming in, the file index.php is executed and this file will have access to the original request in $_GET['url'], so it can do anything it wants with it.



      Primarily you put these rewrite rules into your web server configuration file. Apache also allows* you to put them into a file called .htaccess within your document root (i.e. next to your .php files).



      * If allowed by the primary Apache configuration file; it's optional, but often enabled.



      What mod_rewrite does not do



      mod_rewrite does not magically make all your URLs "pretty". This is a common misunderstanding. If you have this link in your web site:



      <a href="/my/ugly/link.php?is=not&amp;very=pretty">


      there's nothing mod_rewrite can do to make that pretty. In order to make this a pretty link, you have to:




      1. Change the link to a pretty link:



        <a href="/my/pretty/link">


      2. Use mod_rewrite on the server to handle the request to the URL /my/pretty/link using any one of the methods described above.


      (One could use mod_substitute in conjunction to transform outgoing HTML pages and their contained links. Though this is usally more effort than just updating your HTML resources.)



      There's a lot mod_rewrite can do and very complex matching rules you can create, including chaining several rewrites, proxying requests to a completely different service or machine, returning specific HTTP status codes as responses, redirecting requests etc. It's very powerful and can be used to great good if you understand the fundamental HTTP request-response mechanism. It does not automatically make your links pretty.



      See the official documentation for all the possible flags and options.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited May 15 '18 at 16:03

























      answered Dec 13 '13 at 10:07









      decezedeceze

      399k63544701




      399k63544701







      • 5





        Maybe mention the FallbackResource directive introduced in version 2.2.16 as the preffered way of rewriting to a dispatcher.

        – Darsstar
        Dec 13 '13 at 10:31













      • 5





        Maybe mention the FallbackResource directive introduced in version 2.2.16 as the preffered way of rewriting to a dispatcher.

        – Darsstar
        Dec 13 '13 at 10:31








      5




      5





      Maybe mention the FallbackResource directive introduced in version 2.2.16 as the preffered way of rewriting to a dispatcher.

      – Darsstar
      Dec 13 '13 at 10:31






      Maybe mention the FallbackResource directive introduced in version 2.2.16 as the preffered way of rewriting to a dispatcher.

      – Darsstar
      Dec 13 '13 at 10:31














      72














      To expand on deceze's answer, I wanted to provide a few examples and explanation of some other mod_rewrite functionality.



      All of the below examples assume that you have already included RewriteEngine On in your .htaccess file.



      Rewrite Example



      Lets take this example:



      RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]


      The rule is split into 4 sections:




      1. RewriteRule - starts the rewrite rule


      2. ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ - This is called the pattern, however I'll just refer to it as the left hand side of the rule - what you want to rewrite from


      3. blog/index.php?id=$1&title=$2 - called the substitution, or right hand side of a rewrite rule - what you want to rewrite to


      4. [NC,L,QSA] are flags for the rewrite rule, separated by a comma, which I will explain more on later

      The above rewrite would allow you to link to something like /blog/1/foo/ and it would actually load /blog/index.php?id=1&title=foo.



      Left hand side of the rule




      • ^ indicates the start of the page name - so it will rewrite example.com/blog/... but not example.com/foo/blog/...

      • Each set of (…) parentheses represents a regular expression that we can capture as a variable in the right hand side of the rule. In this example:

        • The first set of brackets - ([0-9]+) - matches a string with a minimum of 1 character in length and with only numeric values (i.e. 0-9). This can be referenced with $1 in the right hand side of the rule

        • The second set of parentheses matches a string with a minimum of 1 character in length, containing only alphanumeric characters (A-Z, a-z, or 0-9) or - or + (note + is escaped with a backslash as without escaping it this will execute as a regex repetition character). This can be referenced with $2 in the right hand side of the rule



      • ? means that the preceding character is optional, so in this case both /blog/1/foo/ and /blog/1/foo would rewrite to the same place


      • $ indicates this is the end of the string we want to match

      Flags



      These are options that are added in square brackets at the end of your rewrite rule to specify certain conditions. Again, there are a lot of different flags which you can read up on in the documentation, but I'll go through some of the more common flags:



      NC


      The no case flag means that the rewrite rule is case insensitive, so for the example rule above this would mean that both /blog/1/foo/ and /BLOG/1/foo/ (or any variation of this) would be matched.



      L


      The last flag indicates that this is the last rule that should be processed. This means that if and only if this rule matches, no further rules will be evaluated in the current rewrite processing run. If the rule does not match, all other rules will be tried in order as usual. If you do not set the L flag, all following rules will be applied to the rewritten URL afterwards.



      END


      Since Apache 2.4 you can also use the [END] flag. A matching rule with it will completely terminate further alias/rewrite processing. (Whereas the [L] flag can oftentimes trigger a second round, for example when rewriting into or out of subdirectories.)



      QSA


      The query string append flag allows us to pass in extra variables to the specified URL which will get added to the original get parameters. For our example this means that something like /blog/1/foo/?comments=15 would load /blog/index.php?id=1&title=foo&comments=15



      R


      This flag isn't one I used in the example above, but is one I thought is worth mentioning. This allows you to specify a http redirect, with the option to include a status code (e.g. R=301). For example if you wanted to do a 301 redirect on /myblog/ to /blog/ you would simply write a rule something like this:



      RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]


      Rewrite Conditions



      Rewrite conditions make rewrites even more powerful, allowing you to specify rewrites for more specific situations. There are a lot of conditions which you can read about in the documentation, but I'll touch on a few common examples and explain them:



      # if the host doesn't start with www. then add it and redirect
      RewriteCond %HTTP_HOST !^www.
      RewriteRule ^ http://www.%HTTP_HOST%REQUEST_URI [L,R=301]


      This is a very common practice, which will prepend your domain with www. (if it isn't there already) and execute a 301 redirect. For example, loading up http://example.com/blog/ it would redirect you to http://www.example.com/blog/



      # if it cant find the image, try find the image on another domain
      RewriteCond %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC]
      RewriteCond %REQUEST_FILENAME !-f
      RewriteCond %REQUEST_FILENAME !-d
      RewriteRule (.*)$ http://www.example.com/$1 [L]


      This is slightly less common, but is a good example of a rule that doesn't execute if the filename is a directory or file that exists on the server.




      • %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC] will only execute the rewrite for files with a file extension of jpg, jpeg, gif or png (case insensitive).


      • %REQUEST_FILENAME !-f will check to see if the file exists on the current server, and only execute the rewrite if it doesn't


      • %REQUEST_FILENAME !-d will check to see if the file exists on the current server, and only execute the rewrite if it doesn't

      • The rewrite will attempt to load the same file on another domain





      share|improve this answer





























        72














        To expand on deceze's answer, I wanted to provide a few examples and explanation of some other mod_rewrite functionality.



        All of the below examples assume that you have already included RewriteEngine On in your .htaccess file.



        Rewrite Example



        Lets take this example:



        RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]


        The rule is split into 4 sections:




        1. RewriteRule - starts the rewrite rule


        2. ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ - This is called the pattern, however I'll just refer to it as the left hand side of the rule - what you want to rewrite from


        3. blog/index.php?id=$1&title=$2 - called the substitution, or right hand side of a rewrite rule - what you want to rewrite to


        4. [NC,L,QSA] are flags for the rewrite rule, separated by a comma, which I will explain more on later

        The above rewrite would allow you to link to something like /blog/1/foo/ and it would actually load /blog/index.php?id=1&title=foo.



        Left hand side of the rule




        • ^ indicates the start of the page name - so it will rewrite example.com/blog/... but not example.com/foo/blog/...

        • Each set of (…) parentheses represents a regular expression that we can capture as a variable in the right hand side of the rule. In this example:

          • The first set of brackets - ([0-9]+) - matches a string with a minimum of 1 character in length and with only numeric values (i.e. 0-9). This can be referenced with $1 in the right hand side of the rule

          • The second set of parentheses matches a string with a minimum of 1 character in length, containing only alphanumeric characters (A-Z, a-z, or 0-9) or - or + (note + is escaped with a backslash as without escaping it this will execute as a regex repetition character). This can be referenced with $2 in the right hand side of the rule



        • ? means that the preceding character is optional, so in this case both /blog/1/foo/ and /blog/1/foo would rewrite to the same place


        • $ indicates this is the end of the string we want to match

        Flags



        These are options that are added in square brackets at the end of your rewrite rule to specify certain conditions. Again, there are a lot of different flags which you can read up on in the documentation, but I'll go through some of the more common flags:



        NC


        The no case flag means that the rewrite rule is case insensitive, so for the example rule above this would mean that both /blog/1/foo/ and /BLOG/1/foo/ (or any variation of this) would be matched.



        L


        The last flag indicates that this is the last rule that should be processed. This means that if and only if this rule matches, no further rules will be evaluated in the current rewrite processing run. If the rule does not match, all other rules will be tried in order as usual. If you do not set the L flag, all following rules will be applied to the rewritten URL afterwards.



        END


        Since Apache 2.4 you can also use the [END] flag. A matching rule with it will completely terminate further alias/rewrite processing. (Whereas the [L] flag can oftentimes trigger a second round, for example when rewriting into or out of subdirectories.)



        QSA


        The query string append flag allows us to pass in extra variables to the specified URL which will get added to the original get parameters. For our example this means that something like /blog/1/foo/?comments=15 would load /blog/index.php?id=1&title=foo&comments=15



        R


        This flag isn't one I used in the example above, but is one I thought is worth mentioning. This allows you to specify a http redirect, with the option to include a status code (e.g. R=301). For example if you wanted to do a 301 redirect on /myblog/ to /blog/ you would simply write a rule something like this:



        RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]


        Rewrite Conditions



        Rewrite conditions make rewrites even more powerful, allowing you to specify rewrites for more specific situations. There are a lot of conditions which you can read about in the documentation, but I'll touch on a few common examples and explain them:



        # if the host doesn't start with www. then add it and redirect
        RewriteCond %HTTP_HOST !^www.
        RewriteRule ^ http://www.%HTTP_HOST%REQUEST_URI [L,R=301]


        This is a very common practice, which will prepend your domain with www. (if it isn't there already) and execute a 301 redirect. For example, loading up http://example.com/blog/ it would redirect you to http://www.example.com/blog/



        # if it cant find the image, try find the image on another domain
        RewriteCond %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC]
        RewriteCond %REQUEST_FILENAME !-f
        RewriteCond %REQUEST_FILENAME !-d
        RewriteRule (.*)$ http://www.example.com/$1 [L]


        This is slightly less common, but is a good example of a rule that doesn't execute if the filename is a directory or file that exists on the server.




        • %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC] will only execute the rewrite for files with a file extension of jpg, jpeg, gif or png (case insensitive).


        • %REQUEST_FILENAME !-f will check to see if the file exists on the current server, and only execute the rewrite if it doesn't


        • %REQUEST_FILENAME !-d will check to see if the file exists on the current server, and only execute the rewrite if it doesn't

        • The rewrite will attempt to load the same file on another domain





        share|improve this answer



























          72












          72








          72







          To expand on deceze's answer, I wanted to provide a few examples and explanation of some other mod_rewrite functionality.



          All of the below examples assume that you have already included RewriteEngine On in your .htaccess file.



          Rewrite Example



          Lets take this example:



          RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]


          The rule is split into 4 sections:




          1. RewriteRule - starts the rewrite rule


          2. ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ - This is called the pattern, however I'll just refer to it as the left hand side of the rule - what you want to rewrite from


          3. blog/index.php?id=$1&title=$2 - called the substitution, or right hand side of a rewrite rule - what you want to rewrite to


          4. [NC,L,QSA] are flags for the rewrite rule, separated by a comma, which I will explain more on later

          The above rewrite would allow you to link to something like /blog/1/foo/ and it would actually load /blog/index.php?id=1&title=foo.



          Left hand side of the rule




          • ^ indicates the start of the page name - so it will rewrite example.com/blog/... but not example.com/foo/blog/...

          • Each set of (…) parentheses represents a regular expression that we can capture as a variable in the right hand side of the rule. In this example:

            • The first set of brackets - ([0-9]+) - matches a string with a minimum of 1 character in length and with only numeric values (i.e. 0-9). This can be referenced with $1 in the right hand side of the rule

            • The second set of parentheses matches a string with a minimum of 1 character in length, containing only alphanumeric characters (A-Z, a-z, or 0-9) or - or + (note + is escaped with a backslash as without escaping it this will execute as a regex repetition character). This can be referenced with $2 in the right hand side of the rule



          • ? means that the preceding character is optional, so in this case both /blog/1/foo/ and /blog/1/foo would rewrite to the same place


          • $ indicates this is the end of the string we want to match

          Flags



          These are options that are added in square brackets at the end of your rewrite rule to specify certain conditions. Again, there are a lot of different flags which you can read up on in the documentation, but I'll go through some of the more common flags:



          NC


          The no case flag means that the rewrite rule is case insensitive, so for the example rule above this would mean that both /blog/1/foo/ and /BLOG/1/foo/ (or any variation of this) would be matched.



          L


          The last flag indicates that this is the last rule that should be processed. This means that if and only if this rule matches, no further rules will be evaluated in the current rewrite processing run. If the rule does not match, all other rules will be tried in order as usual. If you do not set the L flag, all following rules will be applied to the rewritten URL afterwards.



          END


          Since Apache 2.4 you can also use the [END] flag. A matching rule with it will completely terminate further alias/rewrite processing. (Whereas the [L] flag can oftentimes trigger a second round, for example when rewriting into or out of subdirectories.)



          QSA


          The query string append flag allows us to pass in extra variables to the specified URL which will get added to the original get parameters. For our example this means that something like /blog/1/foo/?comments=15 would load /blog/index.php?id=1&title=foo&comments=15



          R


          This flag isn't one I used in the example above, but is one I thought is worth mentioning. This allows you to specify a http redirect, with the option to include a status code (e.g. R=301). For example if you wanted to do a 301 redirect on /myblog/ to /blog/ you would simply write a rule something like this:



          RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]


          Rewrite Conditions



          Rewrite conditions make rewrites even more powerful, allowing you to specify rewrites for more specific situations. There are a lot of conditions which you can read about in the documentation, but I'll touch on a few common examples and explain them:



          # if the host doesn't start with www. then add it and redirect
          RewriteCond %HTTP_HOST !^www.
          RewriteRule ^ http://www.%HTTP_HOST%REQUEST_URI [L,R=301]


          This is a very common practice, which will prepend your domain with www. (if it isn't there already) and execute a 301 redirect. For example, loading up http://example.com/blog/ it would redirect you to http://www.example.com/blog/



          # if it cant find the image, try find the image on another domain
          RewriteCond %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC]
          RewriteCond %REQUEST_FILENAME !-f
          RewriteCond %REQUEST_FILENAME !-d
          RewriteRule (.*)$ http://www.example.com/$1 [L]


          This is slightly less common, but is a good example of a rule that doesn't execute if the filename is a directory or file that exists on the server.




          • %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC] will only execute the rewrite for files with a file extension of jpg, jpeg, gif or png (case insensitive).


          • %REQUEST_FILENAME !-f will check to see if the file exists on the current server, and only execute the rewrite if it doesn't


          • %REQUEST_FILENAME !-d will check to see if the file exists on the current server, and only execute the rewrite if it doesn't

          • The rewrite will attempt to load the same file on another domain





          share|improve this answer















          To expand on deceze's answer, I wanted to provide a few examples and explanation of some other mod_rewrite functionality.



          All of the below examples assume that you have already included RewriteEngine On in your .htaccess file.



          Rewrite Example



          Lets take this example:



          RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]


          The rule is split into 4 sections:




          1. RewriteRule - starts the rewrite rule


          2. ^blog/([0-9]+)/([A-Za-z0-9-+]+)/?$ - This is called the pattern, however I'll just refer to it as the left hand side of the rule - what you want to rewrite from


          3. blog/index.php?id=$1&title=$2 - called the substitution, or right hand side of a rewrite rule - what you want to rewrite to


          4. [NC,L,QSA] are flags for the rewrite rule, separated by a comma, which I will explain more on later

          The above rewrite would allow you to link to something like /blog/1/foo/ and it would actually load /blog/index.php?id=1&title=foo.



          Left hand side of the rule




          • ^ indicates the start of the page name - so it will rewrite example.com/blog/... but not example.com/foo/blog/...

          • Each set of (…) parentheses represents a regular expression that we can capture as a variable in the right hand side of the rule. In this example:

            • The first set of brackets - ([0-9]+) - matches a string with a minimum of 1 character in length and with only numeric values (i.e. 0-9). This can be referenced with $1 in the right hand side of the rule

            • The second set of parentheses matches a string with a minimum of 1 character in length, containing only alphanumeric characters (A-Z, a-z, or 0-9) or - or + (note + is escaped with a backslash as without escaping it this will execute as a regex repetition character). This can be referenced with $2 in the right hand side of the rule



          • ? means that the preceding character is optional, so in this case both /blog/1/foo/ and /blog/1/foo would rewrite to the same place


          • $ indicates this is the end of the string we want to match

          Flags



          These are options that are added in square brackets at the end of your rewrite rule to specify certain conditions. Again, there are a lot of different flags which you can read up on in the documentation, but I'll go through some of the more common flags:



          NC


          The no case flag means that the rewrite rule is case insensitive, so for the example rule above this would mean that both /blog/1/foo/ and /BLOG/1/foo/ (or any variation of this) would be matched.



          L


          The last flag indicates that this is the last rule that should be processed. This means that if and only if this rule matches, no further rules will be evaluated in the current rewrite processing run. If the rule does not match, all other rules will be tried in order as usual. If you do not set the L flag, all following rules will be applied to the rewritten URL afterwards.



          END


          Since Apache 2.4 you can also use the [END] flag. A matching rule with it will completely terminate further alias/rewrite processing. (Whereas the [L] flag can oftentimes trigger a second round, for example when rewriting into or out of subdirectories.)



          QSA


          The query string append flag allows us to pass in extra variables to the specified URL which will get added to the original get parameters. For our example this means that something like /blog/1/foo/?comments=15 would load /blog/index.php?id=1&title=foo&comments=15



          R


          This flag isn't one I used in the example above, but is one I thought is worth mentioning. This allows you to specify a http redirect, with the option to include a status code (e.g. R=301). For example if you wanted to do a 301 redirect on /myblog/ to /blog/ you would simply write a rule something like this:



          RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]


          Rewrite Conditions



          Rewrite conditions make rewrites even more powerful, allowing you to specify rewrites for more specific situations. There are a lot of conditions which you can read about in the documentation, but I'll touch on a few common examples and explain them:



          # if the host doesn't start with www. then add it and redirect
          RewriteCond %HTTP_HOST !^www.
          RewriteRule ^ http://www.%HTTP_HOST%REQUEST_URI [L,R=301]


          This is a very common practice, which will prepend your domain with www. (if it isn't there already) and execute a 301 redirect. For example, loading up http://example.com/blog/ it would redirect you to http://www.example.com/blog/



          # if it cant find the image, try find the image on another domain
          RewriteCond %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC]
          RewriteCond %REQUEST_FILENAME !-f
          RewriteCond %REQUEST_FILENAME !-d
          RewriteRule (.*)$ http://www.example.com/$1 [L]


          This is slightly less common, but is a good example of a rule that doesn't execute if the filename is a directory or file that exists on the server.




          • %REQUEST_URI .(jpg|jpeg|gif|png)$ [NC] will only execute the rewrite for files with a file extension of jpg, jpeg, gif or png (case insensitive).


          • %REQUEST_FILENAME !-f will check to see if the file exists on the current server, and only execute the rewrite if it doesn't


          • %REQUEST_FILENAME !-d will check to see if the file exists on the current server, and only execute the rewrite if it doesn't

          • The rewrite will attempt to load the same file on another domain






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited May 23 '17 at 10:31









          Community

          11




          11










          answered Dec 13 '13 at 12:28









          NickNick

          5,41312545




          5,41312545





















              36














              References



              Stack Overflow has many other great resources to get started:




              • Serverfault: Everything you ever wanted to know about mod_rewrite
                (Keep in mind to remove the slash in ^/ pattern prefixes for .htaccess usage.)

              • Do's and Dont's in Hidden features of mod_rewrite.

              • Look through our most popular mod-rewrite questions and answers.

              • Apache redirecting and remapping guide.

              • AskApache ultimate .htaccess guide

              • And the mod-rewrite tag wiki references.

              And newcomer-friendly regex overviews even:



              • Our regex tag wiki for a syntax compendium.

              • And the short Apache regex summary.

              • Else regexp.info for easy-to-understand basics.

              Oft-used placeholders




              • .* matches anything, even an empty string. You don't want to use this pattern everywhere, but often in the last fallback rule.


              • [^/]+ is more often used for path segments. It matches anything but the forward slash.


              • d+ only matches numeric strings.


              • w+ matches alphanumeric characters. It's basically shorthand for [A-Za-z0-9_].


              • [w-]+ for "slug"-style path segments, using letters, numbers, dash - and _


              • [w-.,]+ adds periods and commas. Prefer an escaped - dash in […] charclasses.


              • . denotes a literal period. Otherwise . outside of […] is placeholder for any symbol.

              Each of these placeholders is usually wrapped in (…) parentheses as capture group. And the whole pattern often in ^………$ start + end markers. Quoting "patterns" is optional.



              RewriteRules



              The following examples are PHP-centric and a bit more incremental, easier to adapt for similar cases.
              They're just summaries, often link to more variations or detailed Q&As.






              • Static mapping
                /contact, /about



                Shortening a few page names to internal file schemes is most simple:



                 RewriteRule ^contact$ templ/contact.html
                RewriteRule ^about$ about.php



              • Numeric identifiers
                /object/123



                Introducing shortcuts like http://example.com/article/531 to existing PHP scripts is also easy. The numeric placeholder can just be remapped to a $_GET parameter:



                 RewriteRule ^article/(d+)$ article-show.php?id=$1
                # └───────────────────────────┘



              • Slug-style placeholders
                /article/with-some-title-slug



                You can easily extend that rule to allow for /article/title-string placeholders:



                 RewriteRule ^article/([w-]+)$ article-show.php?title=$1
                # └────────────────────────────────┘


                Note that your script must be able (or be adapted) to map those titles back to database-ids. RewriteRules alone can't create or guess information out of thin air.




              • Slugs with numeric prefixes
                /readable/123-plus-title



                Therefore you'll often see mixed /article/529-title-slug paths used in practice:



                 RewriteRule ^article/(d+)-([w-]+)$ article.php?id=$1&title=$2
                # └───────────────────────────────┘


                Now you could just skip passing the title=$2 anyway, because your script will typically rely on the database-id anyway. The -title-slug has become arbitrary URL decoration.




              • Uniformity with alternative lists
                /foo/… /bar/… /baz/…



                If you have similar rules for multiple virtual page paths, then you can match and compact them with | alternative lists. And again just reassign them to internal GET parameters:



                 # ┌─────────────────────────┐
                RewriteRule ^(blog|post|user)/(w+)$ disp.php?type=$1&id=$2
                # └───────────────────────────────────┘


                You can split them out into individual RewriteRules should this get too complex.




              • Dispatching related URLs to different backends
                /date/SWITCH/backend



                A more practical use of alternative lists are mapping request paths to distinct scripts. For example to provide uniform URLs for an older and a newer web application based on dates:



                 # ┌─────────────────────────────┐
                # │ ┌───────────┼───────────────┐
                RewriteRule ^blog/(2009|2010|2011)/([d-]+)/?$ old/blog.php?date=$2
                RewriteRule ^blog/(d+)/([d-]+)/?$ modern/blog/index.php?start=$2
                # └──────────────────────────────────────┘


                This simply remaps 2009-2011 posts onto one script, and all other years implicitly to another handler.
                Note the more specific rule coming first. Each script might use different GET params.




              • Other delimiters than just / path slashes
                /user-123-name



                You're most commonly seeing RewriteRules to simulate a virtual directory structure. But you're not forced to be uncreative. You can as well use - hyphens for segmenting or structure.



                 RewriteRule ^user-(d+)$ show.php?what=user&id=$1
                # └──────────────────────────────┘
                # This could use `(w+)` alternatively for user names instead of ids.


                For the also common /wiki:section:Page_Name scheme:



                 RewriteRule ^wiki:(w+):(w+)$ wiki.php?sect=$1&page=$2 
                # └─────┼────────────────────┘ │
                # └────────────────────────────┘


                Occasionally it's suitable to alternate between /-delimiters and : or . in the same rule even. Or have two RewriteRules again to map variants onto different scripts.




              • Optional trailing / slash
                /dir = /dir/



                When opting for directory-style paths, you can make it reachable with and without a final /



                 RewriteRule ^blog/([w-]+)/?$ blog/show.php?id=$1
                # ┗┛


                Now this handles both http://example.com/blog/123 and /blog/123/. And the /?$ approach is easy to append onto any other RewriteRule.




              • Flexible segments for virtual paths
                .*/.*/.*/.*



                Most rules you'll encounter map a constrained set of /…/ resource path segments to individual GET parameters. Some scripts handle a variable number of options however.
                The Apache regexp engine doesn't allow optionalizing an arbitrary number of them. But you can easily expand it into a rule block yourself:



                 Rewriterule ^(w+)/?$ in.php?a=$1
                Rewriterule ^(w+)/(w+)/?$ in.php?a=$1&b=$2
                Rewriterule ^(w+)/(w+)/(w+)/?$ in.php?a=$1&b=$2&c=$3
                # └─────┴─────┴───────────────────┴────┴────┘


                If you need up to five path segments, then copy this scheme along into five rules. You can of course use a more specific [^/]+ placeholder each.
                Here the ordering isn't as important, as neither overlaps. So having the most frequently used paths first is okay.



                Alternatively you can utilize PHPs array parameters via ?p=$1&p=$2&p=3 query string here - if your script merely prefers them pre-split.
                (Though it's more common to just use a catch-all rule, and let the script itself expand the segments out of the REQUEST_URI.)



                See also: How do I transform my URL path segments into query string key-value pairs?




              • Optional segments
                prefix/opt?/.*



                A common variation is to have optional prefixes within a rule. This usually makes sense if you have static strings or more constrained placeholders around:



                 RewriteRule ^(w+)(?:/([^/]+))?/(w+)$ ?main=$1&opt=$2&suffix=$3


                Now the more complex pattern (?:/([^/])+)? there simply wraps a non-capturing (?:…) group, and makes it optional )?. The contained
                placeholder ([^/]+) would be substitution pattern $2, but be empty if there's no middle /…/ path.




              • Capture the remainder
                /prefix/123-capture/…/*/…whatever…



                As said before, you don't often want too generic rewrite patterns. It does however make sense to combine static and specific comparisons with a .* sometimes.



                 RewriteRule ^(specific)/prefix/(d+)(/.*)?$ speci.php?id=$2&otherparams=$2


                This optionalized any /…/…/… trailing path segments. Which then of course requires the handling script to split them up, and variabl-ify extracted parameters
                itself (which is what Web-"MVC" frameworks do).




              • Trailing file "extensions"
                /old/path.HTML



                URLs don't really have file extensions. Which is what this entire reference is about (= URLs are virtual locators, not necessarily a direct filesystem image).
                However if you had a 1:1 file mapping before, you can craft simpler rules:



                 RewriteRule ^styles/([w.-]+).css$ sass-cache.php?old_fn_base=$1
                RewriteRule ^images/([w.-]+).gif$ png-converter.php?load_from=$2


                Other common uses are remapping obsolete .html paths to newer .php handlers, or just aliasing directory names only for individual (actual/real) files.




              • Ping-Pong (redirects and rewrites in unison)
                /ugly.html ←→ /pretty



                So at some point you're rewriting your HTML pages to carry only pretty links, as outlined by deceze.
                Meanwhile you'll still receive requests for the old paths, sometimes even from bookmarks. As workaround, you can ping-pong browsers to display/establish
                the new URLs.



                This common trick involves sending a 30x/Location redirect whenever an incoming URL follows the obsolete/ugly naming scheme.
                Browsers will then rerequest the new/pretty URL, which afterwards is rewritten (just internally) to the original or new location.



                 # redirect browser for old/ugly incoming paths
                RewriteRule ^old/teams.html$ /teams [R=301,QSA,END]

                # internally remap already-pretty incoming request
                RewriteRule ^teams$ teams.php [QSA,END]


                Note how this example just uses [END] instead of [L] to safely alternate. For older Apache 2.2 versions you can use other workarounds, besides also remapping
                query string parameters for example:
                Redirect ugly to pretty URL, remap back to the ugly path, without infinite loops




              • Spaces in patterns
                /this+that+



                It's not that pretty in browser address bars, but you can use spaces in URLs. For rewrite patterns use backslash-escaped spaces.
                Else just "-quote the whole pattern or substitution:



                 RewriteRule "^this [w ]+/(.*)$" "index.php?id=$1" [L]


                Clients serialize URLs with + or %20 for spaces. Yet in RewriteRules they're interpreted with literal characters for all relative path segments.



              Frequent duplicates:




              • Catch-all for a central dispatcher / front-controller script



                 RewriteCond %REQUEST_URI !-f
                RewriteCond %REQUEST_URI !-d
                RewriteRule ^.*$ index.php [L]


                Which is often used by PHP frameworks or WebCMS / portal scripts. The actual path splitting then is handled in PHP using $_SERVER["REQUEST_URI"]. So conceptionally it's pretty much the opposite of URL handling "per mod_rewrite". (Just use FallBackResource instead.)




              • Remove www. from hostname



                Note that this doesn't copy a query string along, etc.



                 # ┌──────────┐
                RewriteCond %HTTP_HOST ^www.(.+)$ [NC] │
                RewriteRule ^(.*)$ http://%1/$1 [R=301,L] │
                # ↓ └───┼────────────┘
                # └───────────────┘


                See also:

                · URL rewriting for different protocols in .htaccess

                · Generic htaccess redirect www to non-www

                · .htaccess - how to force "www." in a generic way?



                Note that RewriteCond/RewriteRule combos can be more complex, with matches (%1 and $1) interacting in both directions even:



                References %1 and $2, %3 between RewriteRule and RewriteCond
                Apache manual - mod_rewrite intro, Copyright 2015 The Apache Software Foundation, AL-2.0




              • Redirect to HTTPS://



                 RewriteCond %SERVER_PORT 80
                RewriteRule ^(.*)$ https://example.com/$1 [R,L]


                See also: https://wiki.apache.org/httpd/RewriteHTTPToHTTPS




              • "Removing" the PHP extension



                 RewriteCond %REQUEST_FILENAME.php -f
                RewriteRule ^(.+)$ $1.php [L] # or [END]


                See also: Removing the .php extension with mod_rewrite




              • Aliasing old .html paths to .php scripts



                See: http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility




              • Rewrite from URL like "/page" to a script such as "/index.php/page"



                See mod_rewrite, php and the .htaccess file




              • Redirect subdomain to a folder



                See How can i get my htaccess to work (subdomains)?



              Prevalent .htaccess pitfalls



              Now take this with a grain of salt. Not every advise can be generalized to all contexts.
              This is just a simple summary of well-known and a few unobvious stumbling blocks:




              • Enable mod_rewrite and .htaccess



                To actually use RewriteRules in per-directory configuration files you must:



                • Check that your server has AllowOverride All enabled. Otherwise your per-directory .htaccess directives will go ignored, and RewriteRules won't work.


                • Obviously have mod_rewrite enabled in your httpd.conf modules section.


                • Prepend each list of rules with RewriteEngine On still. While mod_rewrite is implicitly active in <VirtualHost> and <Directory> sections,
                  the per-directory .htaccess files need it individually summoned.




              • The leading slash ^/ won't match



                You shouldn't start your .htaccess RewriteRule patterns with ^/ normally:



                 RewriteRule ^/article/d+$ …



                This is often seen in old tutorials. And it used to be correct for ancient Apache 1.x versions. Nowadays request paths are conveniently fully directory-relative in .htaccess RewriteRules. Just leave the leading / out.



                · Note that the leading slash is still correct in <VirtualHost> sections though. Which is why you often see it ^/? optionalized for rule parity.

                · Or when using a RewriteCond %REQUEST_URI you'd still match for a leading /.

                · See also Webmaster.SE: When is the leading slash (/) needed in mod_rewrite patterns?





              • <IfModule *> wrappers begone!



                You've probably seen this in many examples:



                <IfModule mod_rewrite.c>
                Rewrite…
                </IfModule>


                • It does make sense in <VirtualHost> sections - if it was combined with another fallback option, such as ScriptAliasMatch. (But nobody ever does that).

                • And it's commonly distributed for default .htaccess rulesets with many open source projects. There it's just meant as fallback, and keeps "ugly" URLs work as default.

                However you don't want that usually in your own .htaccess files.



                • Firstly, mod_rewrite does not randomly disengage. (If it did, you'd have bigger problems).

                • Were it really be disabled, your RewriteRules still wouldn't work anyway.

                • It's meant to prevent HTTP 500 errors. What it usually accomplishes is gracing your users with HTTP 404 errors instead. (Not so much more user-friendly if you think about it.)

                • Practically it just suppresses the more useful log entries, or server notification mails. You'd be none the wiser as to why your RewriteRules never work.


                What seems enticing as generalized safeguard, often turns out to be an obstacle in practice.





              • Don't use RewriteBase unless needed



                Many copy+paste examples contain a RewriteBase / directive. Which happens to be the implicit default anyway. So you don't actually need this. It's a workaround for fancy VirtualHost rewriting schemes, and misguessed DOCUMENT_ROOT paths for some shared hosters.



                It makes sense to use with individual web applications in deeper subdirectories. It can shorten RewriteRule patterns in such cases. Generally it's best to prefer relative path specifiers in per-directory rule sets.



                See also How does RewriteBase work in .htaccess




              • Disable MultiViews when virtual paths overlap



                URL rewriting is primarily used for supporting virtual incoming paths. Commonly you just have one dispatcher script (index.php) or a few individual handlers (articles.php, blog.php, wiki.php, …). The latter might clash with similar virtual RewriteRule paths.



                A request for /article/123 for example could map to article.php with a /123 PATH_INFO implicitly. You'd either have to guard your rules then with the commonplace RewriteCond !-f+!-d, and/or disable PATH_INFO support, or perhaps just disable Options -MultiViews.



                Which is not to say you always have to. Content-Negotiation is just an automatism to virtual resources.




              • Ordering is important



                See Everything you ever wanted to know about mod_rewrite
                if you haven't already. Combining multiple RewriteRules often leads to interaction. This isn't something to prevent habitually per [L] flag, but a scheme you'll embrace once versed.
                You can re-re-rewrite virtual paths from one rule to another, until it reaches an actual target handler.



                Still you'd often want to have the most specific rules (fixed string /forum/… patterns, or more restrictive placeholders [^/.]+) in the early rules.
                Generic slurp-all rules (.*) are better left to the later ones. (An exception is a RewriteCond -f/-d guard as primary block.)




              • Stylesheets and images stop working



                When you introduce virtual directory structures /blog/article/123 this impacts relative resource references in HTML (such as <img src=mouse.png>).
                Which can be solved by:



                • Only using server-absolute references href="/old.html" or src="/logo.png"

                • Often simply by adding <base href="/index"> into your HTML <head> section.
                  This implicitly rebinds relative references to what they were before.

                You could alternatively craft further RewriteRules to rebind .css or .png paths to their original locations.
                But that's both unneeded, or incurs extra redirects and hampers caching.



                See also: CSS, JS and images do not display with pretty url




              • RewriteConds just mask one RewriteRule



                A common misinterpetation is that a RewriteCond blocks multiple RewriteRules (because they're visually arranged together):



                 RewriteCond %SERVER_NAME localhost
                RewriteRule ^secret admin/tools.php
                RewriteRule ^hidden sqladmin.cgi


                Which it doesn't per default. You can chain them using the [S=2] flag. Else you'll have to repeat them. While sometimes you can craft an "inverted" primary rule to [END] the rewrite processing early.




              • QUERY_STRING exempt from RewriteRules



                You can't match RewriteRule index.php?x=y, because mod_rewrite compares just against relative paths per default. You can match them separately however via:



                 RewriteCond %QUERY_STRING b(?:param)=([^&]+)(?:&|$)
                RewriteRule ^add/(.+)$ add/%1/$1 # ←──﹪₁──┘


                See also How can I match query string variables with mod_rewrite?





              • .htaccess vs. <VirtualHost>



                If you're using RewriteRules in a per-directory config file, then worrying about regex performance is pointless. Apache retains
                compiled PCRE patterns longer than a PHP process with a common routing framework. For high-traffic sites you should however consider
                moving rulesets into the vhost server configuration, once they've been battle-tested.



                In this case, prefer the optionalized ^/? directory separator prefix. This allows to move RewriteRules freely between PerDir and server
                config files.




              • Whenever something doesn't work



                Fret not.




                • Compare access.log and error.log



                  Often you can figure out how a RewriteRule misbehaves just from looking at your error.log and access.log.
                  Correlate access times to see which request path originally came in, and which path/file Apache couldn't resolve to (error 404/500).



                  This doesn't tell you which RewriteRule is the culprit. But inaccessible final paths like /docroot/21-.itle?index.php may give away where to inspect further.
                  Otherwise disable rules until you get some predictable paths.




                • Enable the RewriteLog



                  See Apache RewriteLog docs. For debugging you can enable it in the vhost sections:



                  # Apache 2.2
                  RewriteLogLevel 5
                  RewriteLog /tmp/rewrite.log

                  # Apache 2.4
                  LogLevel alert rewrite:trace5
                  #ErrorLog /tmp/rewrite.log


                  That yields a detailed summary of how incoming request paths get modified by each rule:



                  [..] applying pattern '^test_.*$' to uri 'index.php'
                  [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
                  [..] applying pattern '^index.php$' to uri 'index.php'


                  Which helps to narrow down overly generic rules and regex mishaps.



                  See also:

                  · .htaccess not working (mod_rewrite)

                  · Tips for debugging .htaccess rewrite rules




                • Before asking your own question



                  As you might know, Stack Overflow is very suitable for asking questions on mod_rewrite. Make them on-topic
                  by including prior research and attempts (avoid redundant answers), demonstrate basic regex understanding, and:



                  • Include full examples of input URLs, falsly rewritten target paths, your real directory structure.

                  • The complete RewriteRule set, but also single out the presumed defective one.

                  • Apache and PHP versions, OS type, filesystem, DOCUMENT_ROOT, and PHPs $_SERVER environment if it's about a parameter mismatch.

                  • An excerpt from your access.log and error.log to verify what the existing rules resolved to. Better yet, a rewrite.log summary.

                  This nets quicker and more exact answers, and makes them more useful to others.





              • Comment your .htaccess



                If you copy examples from somewhere, take care to include a # comment and origin link. While it's merely bad manners to omit attribution,
                it often really hurts maintenance later. Document any code or tutorial source. In particular while unversed you should be
                all the more interested in not treating them like magic blackboxes.




              • It's not "SEO"-URLs



                Disclaimer: Just a pet peeve. You often hear pretty URL rewriting schemes referred to as "SEO" links or something. While this is useful for googling examples, it's a dated misnomer.



                None of the modern search engines are really disturbed by .html and .php in path segments, or ?id=123 query strings for that matter. Search engines of old, such as AltaVista, did avoid crawling websites with potentially ambigious access paths. Modern crawlers are often even craving for deep web resources.



                What "pretty" URLs should conceptionally be used for is making websites user-friendly.



                1. Having readable and obvious resource schemes.

                2. Ensuring URLs are long-lived (AKA permalinks).

                3. Providing discoverability through /common/tree/nesting.

                However don't sacrifice unique requirements for conformism.



              Tools



              There are various online tools to generate RewriteRules for most GET-parameterish URLs:



              • http://www.generateit.net/mod-rewrite/index.php

              • http://www.ipdistance.com/mod_rewrite.php

              • http://webtools.live2support.com/misc_rewrite.php

              Mostly just output [^/]+ generic placeholders, but likely suffices for trivial sites.






              share|improve this answer

























              • Still needs a bit rewriting, more links, and the many subheadlines are somewhat obnoxious. There's some overlap with the other answers here, so can be cut down perhaps. It's mainly about the visual examples though, and that list of common gotchas.

                – mario
                Jul 7 '15 at 22:07






              • 2





                Didn't saw such a beauty of an answer for a long time! My eyes are glowing while I'm reading it. Please don't stop posting such answers :)

                – Rizier123
                Jul 8 '15 at 18:56











              • Excellent post. Made me understand the basic concepts of mod_rewrite very quickly!

                – breez
                Dec 9 '16 at 11:20















              36














              References



              Stack Overflow has many other great resources to get started:




              • Serverfault: Everything you ever wanted to know about mod_rewrite
                (Keep in mind to remove the slash in ^/ pattern prefixes for .htaccess usage.)

              • Do's and Dont's in Hidden features of mod_rewrite.

              • Look through our most popular mod-rewrite questions and answers.

              • Apache redirecting and remapping guide.

              • AskApache ultimate .htaccess guide

              • And the mod-rewrite tag wiki references.

              And newcomer-friendly regex overviews even:



              • Our regex tag wiki for a syntax compendium.

              • And the short Apache regex summary.

              • Else regexp.info for easy-to-understand basics.

              Oft-used placeholders




              • .* matches anything, even an empty string. You don't want to use this pattern everywhere, but often in the last fallback rule.


              • [^/]+ is more often used for path segments. It matches anything but the forward slash.


              • d+ only matches numeric strings.


              • w+ matches alphanumeric characters. It's basically shorthand for [A-Za-z0-9_].


              • [w-]+ for "slug"-style path segments, using letters, numbers, dash - and _


              • [w-.,]+ adds periods and commas. Prefer an escaped - dash in […] charclasses.


              • . denotes a literal period. Otherwise . outside of […] is placeholder for any symbol.

              Each of these placeholders is usually wrapped in (…) parentheses as capture group. And the whole pattern often in ^………$ start + end markers. Quoting "patterns" is optional.



              RewriteRules



              The following examples are PHP-centric and a bit more incremental, easier to adapt for similar cases.
              They're just summaries, often link to more variations or detailed Q&As.






              • Static mapping
                /contact, /about



                Shortening a few page names to internal file schemes is most simple:



                 RewriteRule ^contact$ templ/contact.html
                RewriteRule ^about$ about.php



              • Numeric identifiers
                /object/123



                Introducing shortcuts like http://example.com/article/531 to existing PHP scripts is also easy. The numeric placeholder can just be remapped to a $_GET parameter:



                 RewriteRule ^article/(d+)$ article-show.php?id=$1
                # └───────────────────────────┘



              • Slug-style placeholders
                /article/with-some-title-slug



                You can easily extend that rule to allow for /article/title-string placeholders:



                 RewriteRule ^article/([w-]+)$ article-show.php?title=$1
                # └────────────────────────────────┘


                Note that your script must be able (or be adapted) to map those titles back to database-ids. RewriteRules alone can't create or guess information out of thin air.




              • Slugs with numeric prefixes
                /readable/123-plus-title



                Therefore you'll often see mixed /article/529-title-slug paths used in practice:



                 RewriteRule ^article/(d+)-([w-]+)$ article.php?id=$1&title=$2
                # └───────────────────────────────┘


                Now you could just skip passing the title=$2 anyway, because your script will typically rely on the database-id anyway. The -title-slug has become arbitrary URL decoration.




              • Uniformity with alternative lists
                /foo/… /bar/… /baz/…



                If you have similar rules for multiple virtual page paths, then you can match and compact them with | alternative lists. And again just reassign them to internal GET parameters:



                 # ┌─────────────────────────┐
                RewriteRule ^(blog|post|user)/(w+)$ disp.php?type=$1&id=$2
                # └───────────────────────────────────┘


                You can split them out into individual RewriteRules should this get too complex.




              • Dispatching related URLs to different backends
                /date/SWITCH/backend



                A more practical use of alternative lists are mapping request paths to distinct scripts. For example to provide uniform URLs for an older and a newer web application based on dates:



                 # ┌─────────────────────────────┐
                # │ ┌───────────┼───────────────┐
                RewriteRule ^blog/(2009|2010|2011)/([d-]+)/?$ old/blog.php?date=$2
                RewriteRule ^blog/(d+)/([d-]+)/?$ modern/blog/index.php?start=$2
                # └──────────────────────────────────────┘


                This simply remaps 2009-2011 posts onto one script, and all other years implicitly to another handler.
                Note the more specific rule coming first. Each script might use different GET params.




              • Other delimiters than just / path slashes
                /user-123-name



                You're most commonly seeing RewriteRules to simulate a virtual directory structure. But you're not forced to be uncreative. You can as well use - hyphens for segmenting or structure.



                 RewriteRule ^user-(d+)$ show.php?what=user&id=$1
                # └──────────────────────────────┘
                # This could use `(w+)` alternatively for user names instead of ids.


                For the also common /wiki:section:Page_Name scheme:



                 RewriteRule ^wiki:(w+):(w+)$ wiki.php?sect=$1&page=$2 
                # └─────┼────────────────────┘ │
                # └────────────────────────────┘


                Occasionally it's suitable to alternate between /-delimiters and : or . in the same rule even. Or have two RewriteRules again to map variants onto different scripts.




              • Optional trailing / slash
                /dir = /dir/



                When opting for directory-style paths, you can make it reachable with and without a final /



                 RewriteRule ^blog/([w-]+)/?$ blog/show.php?id=$1
                # ┗┛


                Now this handles both http://example.com/blog/123 and /blog/123/. And the /?$ approach is easy to append onto any other RewriteRule.




              • Flexible segments for virtual paths
                .*/.*/.*/.*



                Most rules you'll encounter map a constrained set of /…/ resource path segments to individual GET parameters. Some scripts handle a variable number of options however.
                The Apache regexp engine doesn't allow optionalizing an arbitrary number of them. But you can easily expand it into a rule block yourself:



                 Rewriterule ^(w+)/?$ in.php?a=$1
                Rewriterule ^(w+)/(w+)/?$ in.php?a=$1&b=$2
                Rewriterule ^(w+)/(w+)/(w+)/?$ in.php?a=$1&b=$2&c=$3
                # └─────┴─────┴───────────────────┴────┴────┘


                If you need up to five path segments, then copy this scheme along into five rules. You can of course use a more specific [^/]+ placeholder each.
                Here the ordering isn't as important, as neither overlaps. So having the most frequently used paths first is okay.



                Alternatively you can utilize PHPs array parameters via ?p=$1&p=$2&p=3 query string here - if your script merely prefers them pre-split.
                (Though it's more common to just use a catch-all rule, and let the script itself expand the segments out of the REQUEST_URI.)



                See also: How do I transform my URL path segments into query string key-value pairs?




              • Optional segments
                prefix/opt?/.*



                A common variation is to have optional prefixes within a rule. This usually makes sense if you have static strings or more constrained placeholders around:



                 RewriteRule ^(w+)(?:/([^/]+))?/(w+)$ ?main=$1&opt=$2&suffix=$3


                Now the more complex pattern (?:/([^/])+)? there simply wraps a non-capturing (?:…) group, and makes it optional )?. The contained
                placeholder ([^/]+) would be substitution pattern $2, but be empty if there's no middle /…/ path.




              • Capture the remainder
                /prefix/123-capture/…/*/…whatever…



                As said before, you don't often want too generic rewrite patterns. It does however make sense to combine static and specific comparisons with a .* sometimes.



                 RewriteRule ^(specific)/prefix/(d+)(/.*)?$ speci.php?id=$2&otherparams=$2


                This optionalized any /…/…/… trailing path segments. Which then of course requires the handling script to split them up, and variabl-ify extracted parameters
                itself (which is what Web-"MVC" frameworks do).




              • Trailing file "extensions"
                /old/path.HTML



                URLs don't really have file extensions. Which is what this entire reference is about (= URLs are virtual locators, not necessarily a direct filesystem image).
                However if you had a 1:1 file mapping before, you can craft simpler rules:



                 RewriteRule ^styles/([w.-]+).css$ sass-cache.php?old_fn_base=$1
                RewriteRule ^images/([w.-]+).gif$ png-converter.php?load_from=$2


                Other common uses are remapping obsolete .html paths to newer .php handlers, or just aliasing directory names only for individual (actual/real) files.




              • Ping-Pong (redirects and rewrites in unison)
                /ugly.html ←→ /pretty



                So at some point you're rewriting your HTML pages to carry only pretty links, as outlined by deceze.
                Meanwhile you'll still receive requests for the old paths, sometimes even from bookmarks. As workaround, you can ping-pong browsers to display/establish
                the new URLs.



                This common trick involves sending a 30x/Location redirect whenever an incoming URL follows the obsolete/ugly naming scheme.
                Browsers will then rerequest the new/pretty URL, which afterwards is rewritten (just internally) to the original or new location.



                 # redirect browser for old/ugly incoming paths
                RewriteRule ^old/teams.html$ /teams [R=301,QSA,END]

                # internally remap already-pretty incoming request
                RewriteRule ^teams$ teams.php [QSA,END]


                Note how this example just uses [END] instead of [L] to safely alternate. For older Apache 2.2 versions you can use other workarounds, besides also remapping
                query string parameters for example:
                Redirect ugly to pretty URL, remap back to the ugly path, without infinite loops




              • Spaces in patterns
                /this+that+



                It's not that pretty in browser address bars, but you can use spaces in URLs. For rewrite patterns use backslash-escaped spaces.
                Else just "-quote the whole pattern or substitution:



                 RewriteRule "^this [w ]+/(.*)$" "index.php?id=$1" [L]


                Clients serialize URLs with + or %20 for spaces. Yet in RewriteRules they're interpreted with literal characters for all relative path segments.



              Frequent duplicates:




              • Catch-all for a central dispatcher / front-controller script



                 RewriteCond %REQUEST_URI !-f
                RewriteCond %REQUEST_URI !-d
                RewriteRule ^.*$ index.php [L]


                Which is often used by PHP frameworks or WebCMS / portal scripts. The actual path splitting then is handled in PHP using $_SERVER["REQUEST_URI"]. So conceptionally it's pretty much the opposite of URL handling "per mod_rewrite". (Just use FallBackResource instead.)




              • Remove www. from hostname



                Note that this doesn't copy a query string along, etc.



                 # ┌──────────┐
                RewriteCond %HTTP_HOST ^www.(.+)$ [NC] │
                RewriteRule ^(.*)$ http://%1/$1 [R=301,L] │
                # ↓ └───┼────────────┘
                # └───────────────┘


                See also:

                · URL rewriting for different protocols in .htaccess

                · Generic htaccess redirect www to non-www

                · .htaccess - how to force "www." in a generic way?



                Note that RewriteCond/RewriteRule combos can be more complex, with matches (%1 and $1) interacting in both directions even:



                References %1 and $2, %3 between RewriteRule and RewriteCond
                Apache manual - mod_rewrite intro, Copyright 2015 The Apache Software Foundation, AL-2.0




              • Redirect to HTTPS://



                 RewriteCond %SERVER_PORT 80
                RewriteRule ^(.*)$ https://example.com/$1 [R,L]


                See also: https://wiki.apache.org/httpd/RewriteHTTPToHTTPS




              • "Removing" the PHP extension



                 RewriteCond %REQUEST_FILENAME.php -f
                RewriteRule ^(.+)$ $1.php [L] # or [END]


                See also: Removing the .php extension with mod_rewrite




              • Aliasing old .html paths to .php scripts



                See: http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility




              • Rewrite from URL like "/page" to a script such as "/index.php/page"



                See mod_rewrite, php and the .htaccess file




              • Redirect subdomain to a folder



                See How can i get my htaccess to work (subdomains)?



              Prevalent .htaccess pitfalls



              Now take this with a grain of salt. Not every advise can be generalized to all contexts.
              This is just a simple summary of well-known and a few unobvious stumbling blocks:




              • Enable mod_rewrite and .htaccess



                To actually use RewriteRules in per-directory configuration files you must:



                • Check that your server has AllowOverride All enabled. Otherwise your per-directory .htaccess directives will go ignored, and RewriteRules won't work.


                • Obviously have mod_rewrite enabled in your httpd.conf modules section.


                • Prepend each list of rules with RewriteEngine On still. While mod_rewrite is implicitly active in <VirtualHost> and <Directory> sections,
                  the per-directory .htaccess files need it individually summoned.




              • The leading slash ^/ won't match



                You shouldn't start your .htaccess RewriteRule patterns with ^/ normally:



                 RewriteRule ^/article/d+$ …



                This is often seen in old tutorials. And it used to be correct for ancient Apache 1.x versions. Nowadays request paths are conveniently fully directory-relative in .htaccess RewriteRules. Just leave the leading / out.



                · Note that the leading slash is still correct in <VirtualHost> sections though. Which is why you often see it ^/? optionalized for rule parity.

                · Or when using a RewriteCond %REQUEST_URI you'd still match for a leading /.

                · See also Webmaster.SE: When is the leading slash (/) needed in mod_rewrite patterns?





              • <IfModule *> wrappers begone!



                You've probably seen this in many examples:



                <IfModule mod_rewrite.c>
                Rewrite…
                </IfModule>


                • It does make sense in <VirtualHost> sections - if it was combined with another fallback option, such as ScriptAliasMatch. (But nobody ever does that).

                • And it's commonly distributed for default .htaccess rulesets with many open source projects. There it's just meant as fallback, and keeps "ugly" URLs work as default.

                However you don't want that usually in your own .htaccess files.



                • Firstly, mod_rewrite does not randomly disengage. (If it did, you'd have bigger problems).

                • Were it really be disabled, your RewriteRules still wouldn't work anyway.

                • It's meant to prevent HTTP 500 errors. What it usually accomplishes is gracing your users with HTTP 404 errors instead. (Not so much more user-friendly if you think about it.)

                • Practically it just suppresses the more useful log entries, or server notification mails. You'd be none the wiser as to why your RewriteRules never work.


                What seems enticing as generalized safeguard, often turns out to be an obstacle in practice.





              • Don't use RewriteBase unless needed



                Many copy+paste examples contain a RewriteBase / directive. Which happens to be the implicit default anyway. So you don't actually need this. It's a workaround for fancy VirtualHost rewriting schemes, and misguessed DOCUMENT_ROOT paths for some shared hosters.



                It makes sense to use with individual web applications in deeper subdirectories. It can shorten RewriteRule patterns in such cases. Generally it's best to prefer relative path specifiers in per-directory rule sets.



                See also How does RewriteBase work in .htaccess




              • Disable MultiViews when virtual paths overlap



                URL rewriting is primarily used for supporting virtual incoming paths. Commonly you just have one dispatcher script (index.php) or a few individual handlers (articles.php, blog.php, wiki.php, …). The latter might clash with similar virtual RewriteRule paths.



                A request for /article/123 for example could map to article.php with a /123 PATH_INFO implicitly. You'd either have to guard your rules then with the commonplace RewriteCond !-f+!-d, and/or disable PATH_INFO support, or perhaps just disable Options -MultiViews.



                Which is not to say you always have to. Content-Negotiation is just an automatism to virtual resources.




              • Ordering is important



                See Everything you ever wanted to know about mod_rewrite
                if you haven't already. Combining multiple RewriteRules often leads to interaction. This isn't something to prevent habitually per [L] flag, but a scheme you'll embrace once versed.
                You can re-re-rewrite virtual paths from one rule to another, until it reaches an actual target handler.



                Still you'd often want to have the most specific rules (fixed string /forum/… patterns, or more restrictive placeholders [^/.]+) in the early rules.
                Generic slurp-all rules (.*) are better left to the later ones. (An exception is a RewriteCond -f/-d guard as primary block.)




              • Stylesheets and images stop working



                When you introduce virtual directory structures /blog/article/123 this impacts relative resource references in HTML (such as <img src=mouse.png>).
                Which can be solved by:



                • Only using server-absolute references href="/old.html" or src="/logo.png"

                • Often simply by adding <base href="/index"> into your HTML <head> section.
                  This implicitly rebinds relative references to what they were before.

                You could alternatively craft further RewriteRules to rebind .css or .png paths to their original locations.
                But that's both unneeded, or incurs extra redirects and hampers caching.



                See also: CSS, JS and images do not display with pretty url




              • RewriteConds just mask one RewriteRule



                A common misinterpetation is that a RewriteCond blocks multiple RewriteRules (because they're visually arranged together):



                 RewriteCond %SERVER_NAME localhost
                RewriteRule ^secret admin/tools.php
                RewriteRule ^hidden sqladmin.cgi


                Which it doesn't per default. You can chain them using the [S=2] flag. Else you'll have to repeat them. While sometimes you can craft an "inverted" primary rule to [END] the rewrite processing early.




              • QUERY_STRING exempt from RewriteRules



                You can't match RewriteRule index.php?x=y, because mod_rewrite compares just against relative paths per default. You can match them separately however via:



                 RewriteCond %QUERY_STRING b(?:param)=([^&]+)(?:&|$)
                RewriteRule ^add/(.+)$ add/%1/$1 # ←──﹪₁──┘


                See also How can I match query string variables with mod_rewrite?





              • .htaccess vs. <VirtualHost>



                If you're using RewriteRules in a per-directory config file, then worrying about regex performance is pointless. Apache retains
                compiled PCRE patterns longer than a PHP process with a common routing framework. For high-traffic sites you should however consider
                moving rulesets into the vhost server configuration, once they've been battle-tested.



                In this case, prefer the optionalized ^/? directory separator prefix. This allows to move RewriteRules freely between PerDir and server
                config files.




              • Whenever something doesn't work



                Fret not.




                • Compare access.log and error.log



                  Often you can figure out how a RewriteRule misbehaves just from looking at your error.log and access.log.
                  Correlate access times to see which request path originally came in, and which path/file Apache couldn't resolve to (error 404/500).



                  This doesn't tell you which RewriteRule is the culprit. But inaccessible final paths like /docroot/21-.itle?index.php may give away where to inspect further.
                  Otherwise disable rules until you get some predictable paths.




                • Enable the RewriteLog



                  See Apache RewriteLog docs. For debugging you can enable it in the vhost sections:



                  # Apache 2.2
                  RewriteLogLevel 5
                  RewriteLog /tmp/rewrite.log

                  # Apache 2.4
                  LogLevel alert rewrite:trace5
                  #ErrorLog /tmp/rewrite.log


                  That yields a detailed summary of how incoming request paths get modified by each rule:



                  [..] applying pattern '^test_.*$' to uri 'index.php'
                  [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
                  [..] applying pattern '^index.php$' to uri 'index.php'


                  Which helps to narrow down overly generic rules and regex mishaps.



                  See also:

                  · .htaccess not working (mod_rewrite)

                  · Tips for debugging .htaccess rewrite rules




                • Before asking your own question



                  As you might know, Stack Overflow is very suitable for asking questions on mod_rewrite. Make them on-topic
                  by including prior research and attempts (avoid redundant answers), demonstrate basic regex understanding, and:



                  • Include full examples of input URLs, falsly rewritten target paths, your real directory structure.

                  • The complete RewriteRule set, but also single out the presumed defective one.

                  • Apache and PHP versions, OS type, filesystem, DOCUMENT_ROOT, and PHPs $_SERVER environment if it's about a parameter mismatch.

                  • An excerpt from your access.log and error.log to verify what the existing rules resolved to. Better yet, a rewrite.log summary.

                  This nets quicker and more exact answers, and makes them more useful to others.





              • Comment your .htaccess



                If you copy examples from somewhere, take care to include a # comment and origin link. While it's merely bad manners to omit attribution,
                it often really hurts maintenance later. Document any code or tutorial source. In particular while unversed you should be
                all the more interested in not treating them like magic blackboxes.




              • It's not "SEO"-URLs



                Disclaimer: Just a pet peeve. You often hear pretty URL rewriting schemes referred to as "SEO" links or something. While this is useful for googling examples, it's a dated misnomer.



                None of the modern search engines are really disturbed by .html and .php in path segments, or ?id=123 query strings for that matter. Search engines of old, such as AltaVista, did avoid crawling websites with potentially ambigious access paths. Modern crawlers are often even craving for deep web resources.



                What "pretty" URLs should conceptionally be used for is making websites user-friendly.



                1. Having readable and obvious resource schemes.

                2. Ensuring URLs are long-lived (AKA permalinks).

                3. Providing discoverability through /common/tree/nesting.

                However don't sacrifice unique requirements for conformism.



              Tools



              There are various online tools to generate RewriteRules for most GET-parameterish URLs:



              • http://www.generateit.net/mod-rewrite/index.php

              • http://www.ipdistance.com/mod_rewrite.php

              • http://webtools.live2support.com/misc_rewrite.php

              Mostly just output [^/]+ generic placeholders, but likely suffices for trivial sites.






              share|improve this answer

























              • Still needs a bit rewriting, more links, and the many subheadlines are somewhat obnoxious. There's some overlap with the other answers here, so can be cut down perhaps. It's mainly about the visual examples though, and that list of common gotchas.

                – mario
                Jul 7 '15 at 22:07






              • 2





                Didn't saw such a beauty of an answer for a long time! My eyes are glowing while I'm reading it. Please don't stop posting such answers :)

                – Rizier123
                Jul 8 '15 at 18:56











              • Excellent post. Made me understand the basic concepts of mod_rewrite very quickly!

                – breez
                Dec 9 '16 at 11:20













              36












              36








              36







              References



              Stack Overflow has many other great resources to get started:




              • Serverfault: Everything you ever wanted to know about mod_rewrite
                (Keep in mind to remove the slash in ^/ pattern prefixes for .htaccess usage.)

              • Do's and Dont's in Hidden features of mod_rewrite.

              • Look through our most popular mod-rewrite questions and answers.

              • Apache redirecting and remapping guide.

              • AskApache ultimate .htaccess guide

              • And the mod-rewrite tag wiki references.

              And newcomer-friendly regex overviews even:



              • Our regex tag wiki for a syntax compendium.

              • And the short Apache regex summary.

              • Else regexp.info for easy-to-understand basics.

              Oft-used placeholders




              • .* matches anything, even an empty string. You don't want to use this pattern everywhere, but often in the last fallback rule.


              • [^/]+ is more often used for path segments. It matches anything but the forward slash.


              • d+ only matches numeric strings.


              • w+ matches alphanumeric characters. It's basically shorthand for [A-Za-z0-9_].


              • [w-]+ for "slug"-style path segments, using letters, numbers, dash - and _


              • [w-.,]+ adds periods and commas. Prefer an escaped - dash in […] charclasses.


              • . denotes a literal period. Otherwise . outside of […] is placeholder for any symbol.

              Each of these placeholders is usually wrapped in (…) parentheses as capture group. And the whole pattern often in ^………$ start + end markers. Quoting "patterns" is optional.



              RewriteRules



              The following examples are PHP-centric and a bit more incremental, easier to adapt for similar cases.
              They're just summaries, often link to more variations or detailed Q&As.






              • Static mapping
                /contact, /about



                Shortening a few page names to internal file schemes is most simple:



                 RewriteRule ^contact$ templ/contact.html
                RewriteRule ^about$ about.php



              • Numeric identifiers
                /object/123



                Introducing shortcuts like http://example.com/article/531 to existing PHP scripts is also easy. The numeric placeholder can just be remapped to a $_GET parameter:



                 RewriteRule ^article/(d+)$ article-show.php?id=$1
                # └───────────────────────────┘



              • Slug-style placeholders
                /article/with-some-title-slug



                You can easily extend that rule to allow for /article/title-string placeholders:



                 RewriteRule ^article/([w-]+)$ article-show.php?title=$1
                # └────────────────────────────────┘


                Note that your script must be able (or be adapted) to map those titles back to database-ids. RewriteRules alone can't create or guess information out of thin air.




              • Slugs with numeric prefixes
                /readable/123-plus-title



                Therefore you'll often see mixed /article/529-title-slug paths used in practice:



                 RewriteRule ^article/(d+)-([w-]+)$ article.php?id=$1&title=$2
                # └───────────────────────────────┘


                Now you could just skip passing the title=$2 anyway, because your script will typically rely on the database-id anyway. The -title-slug has become arbitrary URL decoration.




              • Uniformity with alternative lists
                /foo/… /bar/… /baz/…



                If you have similar rules for multiple virtual page paths, then you can match and compact them with | alternative lists. And again just reassign them to internal GET parameters:



                 # ┌─────────────────────────┐
                RewriteRule ^(blog|post|user)/(w+)$ disp.php?type=$1&id=$2
                # └───────────────────────────────────┘


                You can split them out into individual RewriteRules should this get too complex.




              • Dispatching related URLs to different backends
                /date/SWITCH/backend



                A more practical use of alternative lists are mapping request paths to distinct scripts. For example to provide uniform URLs for an older and a newer web application based on dates:



                 # ┌─────────────────────────────┐
                # │ ┌───────────┼───────────────┐
                RewriteRule ^blog/(2009|2010|2011)/([d-]+)/?$ old/blog.php?date=$2
                RewriteRule ^blog/(d+)/([d-]+)/?$ modern/blog/index.php?start=$2
                # └──────────────────────────────────────┘


                This simply remaps 2009-2011 posts onto one script, and all other years implicitly to another handler.
                Note the more specific rule coming first. Each script might use different GET params.




              • Other delimiters than just / path slashes
                /user-123-name



                You're most commonly seeing RewriteRules to simulate a virtual directory structure. But you're not forced to be uncreative. You can as well use - hyphens for segmenting or structure.



                 RewriteRule ^user-(d+)$ show.php?what=user&id=$1
                # └──────────────────────────────┘
                # This could use `(w+)` alternatively for user names instead of ids.


                For the also common /wiki:section:Page_Name scheme:



                 RewriteRule ^wiki:(w+):(w+)$ wiki.php?sect=$1&page=$2 
                # └─────┼────────────────────┘ │
                # └────────────────────────────┘


                Occasionally it's suitable to alternate between /-delimiters and : or . in the same rule even. Or have two RewriteRules again to map variants onto different scripts.




              • Optional trailing / slash
                /dir = /dir/



                When opting for directory-style paths, you can make it reachable with and without a final /



                 RewriteRule ^blog/([w-]+)/?$ blog/show.php?id=$1
                # ┗┛


                Now this handles both http://example.com/blog/123 and /blog/123/. And the /?$ approach is easy to append onto any other RewriteRule.




              • Flexible segments for virtual paths
                .*/.*/.*/.*



                Most rules you'll encounter map a constrained set of /…/ resource path segments to individual GET parameters. Some scripts handle a variable number of options however.
                The Apache regexp engine doesn't allow optionalizing an arbitrary number of them. But you can easily expand it into a rule block yourself:



                 Rewriterule ^(w+)/?$ in.php?a=$1
                Rewriterule ^(w+)/(w+)/?$ in.php?a=$1&b=$2
                Rewriterule ^(w+)/(w+)/(w+)/?$ in.php?a=$1&b=$2&c=$3
                # └─────┴─────┴───────────────────┴────┴────┘


                If you need up to five path segments, then copy this scheme along into five rules. You can of course use a more specific [^/]+ placeholder each.
                Here the ordering isn't as important, as neither overlaps. So having the most frequently used paths first is okay.



                Alternatively you can utilize PHPs array parameters via ?p=$1&p=$2&p=3 query string here - if your script merely prefers them pre-split.
                (Though it's more common to just use a catch-all rule, and let the script itself expand the segments out of the REQUEST_URI.)



                See also: How do I transform my URL path segments into query string key-value pairs?




              • Optional segments
                prefix/opt?/.*



                A common variation is to have optional prefixes within a rule. This usually makes sense if you have static strings or more constrained placeholders around:



                 RewriteRule ^(w+)(?:/([^/]+))?/(w+)$ ?main=$1&opt=$2&suffix=$3


                Now the more complex pattern (?:/([^/])+)? there simply wraps a non-capturing (?:…) group, and makes it optional )?. The contained
                placeholder ([^/]+) would be substitution pattern $2, but be empty if there's no middle /…/ path.




              • Capture the remainder
                /prefix/123-capture/…/*/…whatever…



                As said before, you don't often want too generic rewrite patterns. It does however make sense to combine static and specific comparisons with a .* sometimes.



                 RewriteRule ^(specific)/prefix/(d+)(/.*)?$ speci.php?id=$2&otherparams=$2


                This optionalized any /…/…/… trailing path segments. Which then of course requires the handling script to split them up, and variabl-ify extracted parameters
                itself (which is what Web-"MVC" frameworks do).




              • Trailing file "extensions"
                /old/path.HTML



                URLs don't really have file extensions. Which is what this entire reference is about (= URLs are virtual locators, not necessarily a direct filesystem image).
                However if you had a 1:1 file mapping before, you can craft simpler rules:



                 RewriteRule ^styles/([w.-]+).css$ sass-cache.php?old_fn_base=$1
                RewriteRule ^images/([w.-]+).gif$ png-converter.php?load_from=$2


                Other common uses are remapping obsolete .html paths to newer .php handlers, or just aliasing directory names only for individual (actual/real) files.




              • Ping-Pong (redirects and rewrites in unison)
                /ugly.html ←→ /pretty



                So at some point you're rewriting your HTML pages to carry only pretty links, as outlined by deceze.
                Meanwhile you'll still receive requests for the old paths, sometimes even from bookmarks. As workaround, you can ping-pong browsers to display/establish
                the new URLs.



                This common trick involves sending a 30x/Location redirect whenever an incoming URL follows the obsolete/ugly naming scheme.
                Browsers will then rerequest the new/pretty URL, which afterwards is rewritten (just internally) to the original or new location.



                 # redirect browser for old/ugly incoming paths
                RewriteRule ^old/teams.html$ /teams [R=301,QSA,END]

                # internally remap already-pretty incoming request
                RewriteRule ^teams$ teams.php [QSA,END]


                Note how this example just uses [END] instead of [L] to safely alternate. For older Apache 2.2 versions you can use other workarounds, besides also remapping
                query string parameters for example:
                Redirect ugly to pretty URL, remap back to the ugly path, without infinite loops




              • Spaces in patterns
                /this+that+



                It's not that pretty in browser address bars, but you can use spaces in URLs. For rewrite patterns use backslash-escaped spaces.
                Else just "-quote the whole pattern or substitution:



                 RewriteRule "^this [w ]+/(.*)$" "index.php?id=$1" [L]


                Clients serialize URLs with + or %20 for spaces. Yet in RewriteRules they're interpreted with literal characters for all relative path segments.



              Frequent duplicates:




              • Catch-all for a central dispatcher / front-controller script



                 RewriteCond %REQUEST_URI !-f
                RewriteCond %REQUEST_URI !-d
                RewriteRule ^.*$ index.php [L]


                Which is often used by PHP frameworks or WebCMS / portal scripts. The actual path splitting then is handled in PHP using $_SERVER["REQUEST_URI"]. So conceptionally it's pretty much the opposite of URL handling "per mod_rewrite". (Just use FallBackResource instead.)




              • Remove www. from hostname



                Note that this doesn't copy a query string along, etc.



                 # ┌──────────┐
                RewriteCond %HTTP_HOST ^www.(.+)$ [NC] │
                RewriteRule ^(.*)$ http://%1/$1 [R=301,L] │
                # ↓ └───┼────────────┘
                # └───────────────┘


                See also:

                · URL rewriting for different protocols in .htaccess

                · Generic htaccess redirect www to non-www

                · .htaccess - how to force "www." in a generic way?



                Note that RewriteCond/RewriteRule combos can be more complex, with matches (%1 and $1) interacting in both directions even:



                References %1 and $2, %3 between RewriteRule and RewriteCond
                Apache manual - mod_rewrite intro, Copyright 2015 The Apache Software Foundation, AL-2.0




              • Redirect to HTTPS://



                 RewriteCond %SERVER_PORT 80
                RewriteRule ^(.*)$ https://example.com/$1 [R,L]


                See also: https://wiki.apache.org/httpd/RewriteHTTPToHTTPS




              • "Removing" the PHP extension



                 RewriteCond %REQUEST_FILENAME.php -f
                RewriteRule ^(.+)$ $1.php [L] # or [END]


                See also: Removing the .php extension with mod_rewrite




              • Aliasing old .html paths to .php scripts



                See: http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility




              • Rewrite from URL like "/page" to a script such as "/index.php/page"



                See mod_rewrite, php and the .htaccess file




              • Redirect subdomain to a folder



                See How can i get my htaccess to work (subdomains)?



              Prevalent .htaccess pitfalls



              Now take this with a grain of salt. Not every advise can be generalized to all contexts.
              This is just a simple summary of well-known and a few unobvious stumbling blocks:




              • Enable mod_rewrite and .htaccess



                To actually use RewriteRules in per-directory configuration files you must:



                • Check that your server has AllowOverride All enabled. Otherwise your per-directory .htaccess directives will go ignored, and RewriteRules won't work.


                • Obviously have mod_rewrite enabled in your httpd.conf modules section.


                • Prepend each list of rules with RewriteEngine On still. While mod_rewrite is implicitly active in <VirtualHost> and <Directory> sections,
                  the per-directory .htaccess files need it individually summoned.




              • The leading slash ^/ won't match



                You shouldn't start your .htaccess RewriteRule patterns with ^/ normally:



                 RewriteRule ^/article/d+$ …



                This is often seen in old tutorials. And it used to be correct for ancient Apache 1.x versions. Nowadays request paths are conveniently fully directory-relative in .htaccess RewriteRules. Just leave the leading / out.



                · Note that the leading slash is still correct in <VirtualHost> sections though. Which is why you often see it ^/? optionalized for rule parity.

                · Or when using a RewriteCond %REQUEST_URI you'd still match for a leading /.

                · See also Webmaster.SE: When is the leading slash (/) needed in mod_rewrite patterns?





              • <IfModule *> wrappers begone!



                You've probably seen this in many examples:



                <IfModule mod_rewrite.c>
                Rewrite…
                </IfModule>


                • It does make sense in <VirtualHost> sections - if it was combined with another fallback option, such as ScriptAliasMatch. (But nobody ever does that).

                • And it's commonly distributed for default .htaccess rulesets with many open source projects. There it's just meant as fallback, and keeps "ugly" URLs work as default.

                However you don't want that usually in your own .htaccess files.



                • Firstly, mod_rewrite does not randomly disengage. (If it did, you'd have bigger problems).

                • Were it really be disabled, your RewriteRules still wouldn't work anyway.

                • It's meant to prevent HTTP 500 errors. What it usually accomplishes is gracing your users with HTTP 404 errors instead. (Not so much more user-friendly if you think about it.)

                • Practically it just suppresses the more useful log entries, or server notification mails. You'd be none the wiser as to why your RewriteRules never work.


                What seems enticing as generalized safeguard, often turns out to be an obstacle in practice.





              • Don't use RewriteBase unless needed



                Many copy+paste examples contain a RewriteBase / directive. Which happens to be the implicit default anyway. So you don't actually need this. It's a workaround for fancy VirtualHost rewriting schemes, and misguessed DOCUMENT_ROOT paths for some shared hosters.



                It makes sense to use with individual web applications in deeper subdirectories. It can shorten RewriteRule patterns in such cases. Generally it's best to prefer relative path specifiers in per-directory rule sets.



                See also How does RewriteBase work in .htaccess




              • Disable MultiViews when virtual paths overlap



                URL rewriting is primarily used for supporting virtual incoming paths. Commonly you just have one dispatcher script (index.php) or a few individual handlers (articles.php, blog.php, wiki.php, …). The latter might clash with similar virtual RewriteRule paths.



                A request for /article/123 for example could map to article.php with a /123 PATH_INFO implicitly. You'd either have to guard your rules then with the commonplace RewriteCond !-f+!-d, and/or disable PATH_INFO support, or perhaps just disable Options -MultiViews.



                Which is not to say you always have to. Content-Negotiation is just an automatism to virtual resources.




              • Ordering is important



                See Everything you ever wanted to know about mod_rewrite
                if you haven't already. Combining multiple RewriteRules often leads to interaction. This isn't something to prevent habitually per [L] flag, but a scheme you'll embrace once versed.
                You can re-re-rewrite virtual paths from one rule to another, until it reaches an actual target handler.



                Still you'd often want to have the most specific rules (fixed string /forum/… patterns, or more restrictive placeholders [^/.]+) in the early rules.
                Generic slurp-all rules (.*) are better left to the later ones. (An exception is a RewriteCond -f/-d guard as primary block.)




              • Stylesheets and images stop working



                When you introduce virtual directory structures /blog/article/123 this impacts relative resource references in HTML (such as <img src=mouse.png>).
                Which can be solved by:



                • Only using server-absolute references href="/old.html" or src="/logo.png"

                • Often simply by adding <base href="/index"> into your HTML <head> section.
                  This implicitly rebinds relative references to what they were before.

                You could alternatively craft further RewriteRules to rebind .css or .png paths to their original locations.
                But that's both unneeded, or incurs extra redirects and hampers caching.



                See also: CSS, JS and images do not display with pretty url




              • RewriteConds just mask one RewriteRule



                A common misinterpetation is that a RewriteCond blocks multiple RewriteRules (because they're visually arranged together):



                 RewriteCond %SERVER_NAME localhost
                RewriteRule ^secret admin/tools.php
                RewriteRule ^hidden sqladmin.cgi


                Which it doesn't per default. You can chain them using the [S=2] flag. Else you'll have to repeat them. While sometimes you can craft an "inverted" primary rule to [END] the rewrite processing early.




              • QUERY_STRING exempt from RewriteRules



                You can't match RewriteRule index.php?x=y, because mod_rewrite compares just against relative paths per default. You can match them separately however via:



                 RewriteCond %QUERY_STRING b(?:param)=([^&]+)(?:&|$)
                RewriteRule ^add/(.+)$ add/%1/$1 # ←──﹪₁──┘


                See also How can I match query string variables with mod_rewrite?





              • .htaccess vs. <VirtualHost>



                If you're using RewriteRules in a per-directory config file, then worrying about regex performance is pointless. Apache retains
                compiled PCRE patterns longer than a PHP process with a common routing framework. For high-traffic sites you should however consider
                moving rulesets into the vhost server configuration, once they've been battle-tested.



                In this case, prefer the optionalized ^/? directory separator prefix. This allows to move RewriteRules freely between PerDir and server
                config files.




              • Whenever something doesn't work



                Fret not.




                • Compare access.log and error.log



                  Often you can figure out how a RewriteRule misbehaves just from looking at your error.log and access.log.
                  Correlate access times to see which request path originally came in, and which path/file Apache couldn't resolve to (error 404/500).



                  This doesn't tell you which RewriteRule is the culprit. But inaccessible final paths like /docroot/21-.itle?index.php may give away where to inspect further.
                  Otherwise disable rules until you get some predictable paths.




                • Enable the RewriteLog



                  See Apache RewriteLog docs. For debugging you can enable it in the vhost sections:



                  # Apache 2.2
                  RewriteLogLevel 5
                  RewriteLog /tmp/rewrite.log

                  # Apache 2.4
                  LogLevel alert rewrite:trace5
                  #ErrorLog /tmp/rewrite.log


                  That yields a detailed summary of how incoming request paths get modified by each rule:



                  [..] applying pattern '^test_.*$' to uri 'index.php'
                  [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
                  [..] applying pattern '^index.php$' to uri 'index.php'


                  Which helps to narrow down overly generic rules and regex mishaps.



                  See also:

                  · .htaccess not working (mod_rewrite)

                  · Tips for debugging .htaccess rewrite rules




                • Before asking your own question



                  As you might know, Stack Overflow is very suitable for asking questions on mod_rewrite. Make them on-topic
                  by including prior research and attempts (avoid redundant answers), demonstrate basic regex understanding, and:



                  • Include full examples of input URLs, falsly rewritten target paths, your real directory structure.

                  • The complete RewriteRule set, but also single out the presumed defective one.

                  • Apache and PHP versions, OS type, filesystem, DOCUMENT_ROOT, and PHPs $_SERVER environment if it's about a parameter mismatch.

                  • An excerpt from your access.log and error.log to verify what the existing rules resolved to. Better yet, a rewrite.log summary.

                  This nets quicker and more exact answers, and makes them more useful to others.





              • Comment your .htaccess



                If you copy examples from somewhere, take care to include a # comment and origin link. While it's merely bad manners to omit attribution,
                it often really hurts maintenance later. Document any code or tutorial source. In particular while unversed you should be
                all the more interested in not treating them like magic blackboxes.




              • It's not "SEO"-URLs



                Disclaimer: Just a pet peeve. You often hear pretty URL rewriting schemes referred to as "SEO" links or something. While this is useful for googling examples, it's a dated misnomer.



                None of the modern search engines are really disturbed by .html and .php in path segments, or ?id=123 query strings for that matter. Search engines of old, such as AltaVista, did avoid crawling websites with potentially ambigious access paths. Modern crawlers are often even craving for deep web resources.



                What "pretty" URLs should conceptionally be used for is making websites user-friendly.



                1. Having readable and obvious resource schemes.

                2. Ensuring URLs are long-lived (AKA permalinks).

                3. Providing discoverability through /common/tree/nesting.

                However don't sacrifice unique requirements for conformism.



              Tools



              There are various online tools to generate RewriteRules for most GET-parameterish URLs:



              • http://www.generateit.net/mod-rewrite/index.php

              • http://www.ipdistance.com/mod_rewrite.php

              • http://webtools.live2support.com/misc_rewrite.php

              Mostly just output [^/]+ generic placeholders, but likely suffices for trivial sites.






              share|improve this answer















              References



              Stack Overflow has many other great resources to get started:




              • Serverfault: Everything you ever wanted to know about mod_rewrite
                (Keep in mind to remove the slash in ^/ pattern prefixes for .htaccess usage.)

              • Do's and Dont's in Hidden features of mod_rewrite.

              • Look through our most popular mod-rewrite questions and answers.

              • Apache redirecting and remapping guide.

              • AskApache ultimate .htaccess guide

              • And the mod-rewrite tag wiki references.

              And newcomer-friendly regex overviews even:



              • Our regex tag wiki for a syntax compendium.

              • And the short Apache regex summary.

              • Else regexp.info for easy-to-understand basics.

              Oft-used placeholders




              • .* matches anything, even an empty string. You don't want to use this pattern everywhere, but often in the last fallback rule.


              • [^/]+ is more often used for path segments. It matches anything but the forward slash.


              • d+ only matches numeric strings.


              • w+ matches alphanumeric characters. It's basically shorthand for [A-Za-z0-9_].


              • [w-]+ for "slug"-style path segments, using letters, numbers, dash - and _


              • [w-.,]+ adds periods and commas. Prefer an escaped - dash in […] charclasses.


              • . denotes a literal period. Otherwise . outside of […] is placeholder for any symbol.

              Each of these placeholders is usually wrapped in (…) parentheses as capture group. And the whole pattern often in ^………$ start + end markers. Quoting "patterns" is optional.



              RewriteRules



              The following examples are PHP-centric and a bit more incremental, easier to adapt for similar cases.
              They're just summaries, often link to more variations or detailed Q&As.






              • Static mapping
                /contact, /about



                Shortening a few page names to internal file schemes is most simple:



                 RewriteRule ^contact$ templ/contact.html
                RewriteRule ^about$ about.php



              • Numeric identifiers
                /object/123



                Introducing shortcuts like http://example.com/article/531 to existing PHP scripts is also easy. The numeric placeholder can just be remapped to a $_GET parameter:



                 RewriteRule ^article/(d+)$ article-show.php?id=$1
                # └───────────────────────────┘



              • Slug-style placeholders
                /article/with-some-title-slug



                You can easily extend that rule to allow for /article/title-string placeholders:



                 RewriteRule ^article/([w-]+)$ article-show.php?title=$1
                # └────────────────────────────────┘


                Note that your script must be able (or be adapted) to map those titles back to database-ids. RewriteRules alone can't create or guess information out of thin air.




              • Slugs with numeric prefixes
                /readable/123-plus-title



                Therefore you'll often see mixed /article/529-title-slug paths used in practice:



                 RewriteRule ^article/(d+)-([w-]+)$ article.php?id=$1&title=$2
                # └───────────────────────────────┘


                Now you could just skip passing the title=$2 anyway, because your script will typically rely on the database-id anyway. The -title-slug has become arbitrary URL decoration.




              • Uniformity with alternative lists
                /foo/… /bar/… /baz/…



                If you have similar rules for multiple virtual page paths, then you can match and compact them with | alternative lists. And again just reassign them to internal GET parameters:



                 # ┌─────────────────────────┐
                RewriteRule ^(blog|post|user)/(w+)$ disp.php?type=$1&id=$2
                # └───────────────────────────────────┘


                You can split them out into individual RewriteRules should this get too complex.




              • Dispatching related URLs to different backends
                /date/SWITCH/backend



                A more practical use of alternative lists are mapping request paths to distinct scripts. For example to provide uniform URLs for an older and a newer web application based on dates:



                 # ┌─────────────────────────────┐
                # │ ┌───────────┼───────────────┐
                RewriteRule ^blog/(2009|2010|2011)/([d-]+)/?$ old/blog.php?date=$2
                RewriteRule ^blog/(d+)/([d-]+)/?$ modern/blog/index.php?start=$2
                # └──────────────────────────────────────┘


                This simply remaps 2009-2011 posts onto one script, and all other years implicitly to another handler.
                Note the more specific rule coming first. Each script might use different GET params.




              • Other delimiters than just / path slashes
                /user-123-name



                You're most commonly seeing RewriteRules to simulate a virtual directory structure. But you're not forced to be uncreative. You can as well use - hyphens for segmenting or structure.



                 RewriteRule ^user-(d+)$ show.php?what=user&id=$1
                # └──────────────────────────────┘
                # This could use `(w+)` alternatively for user names instead of ids.


                For the also common /wiki:section:Page_Name scheme:



                 RewriteRule ^wiki:(w+):(w+)$ wiki.php?sect=$1&page=$2 
                # └─────┼────────────────────┘ │
                # └────────────────────────────┘


                Occasionally it's suitable to alternate between /-delimiters and : or . in the same rule even. Or have two RewriteRules again to map variants onto different scripts.




              • Optional trailing / slash
                /dir = /dir/



                When opting for directory-style paths, you can make it reachable with and without a final /



                 RewriteRule ^blog/([w-]+)/?$ blog/show.php?id=$1
                # ┗┛


                Now this handles both http://example.com/blog/123 and /blog/123/. And the /?$ approach is easy to append onto any other RewriteRule.




              • Flexible segments for virtual paths
                .*/.*/.*/.*



                Most rules you'll encounter map a constrained set of /…/ resource path segments to individual GET parameters. Some scripts handle a variable number of options however.
                The Apache regexp engine doesn't allow optionalizing an arbitrary number of them. But you can easily expand it into a rule block yourself:



                 Rewriterule ^(w+)/?$ in.php?a=$1
                Rewriterule ^(w+)/(w+)/?$ in.php?a=$1&b=$2
                Rewriterule ^(w+)/(w+)/(w+)/?$ in.php?a=$1&b=$2&c=$3
                # └─────┴─────┴───────────────────┴────┴────┘


                If you need up to five path segments, then copy this scheme along into five rules. You can of course use a more specific [^/]+ placeholder each.
                Here the ordering isn't as important, as neither overlaps. So having the most frequently used paths first is okay.



                Alternatively you can utilize PHPs array parameters via ?p=$1&p=$2&p=3 query string here - if your script merely prefers them pre-split.
                (Though it's more common to just use a catch-all rule, and let the script itself expand the segments out of the REQUEST_URI.)



                See also: How do I transform my URL path segments into query string key-value pairs?




              • Optional segments
                prefix/opt?/.*



                A common variation is to have optional prefixes within a rule. This usually makes sense if you have static strings or more constrained placeholders around:



                 RewriteRule ^(w+)(?:/([^/]+))?/(w+)$ ?main=$1&opt=$2&suffix=$3


                Now the more complex pattern (?:/([^/])+)? there simply wraps a non-capturing (?:…) group, and makes it optional )?. The contained
                placeholder ([^/]+) would be substitution pattern $2, but be empty if there's no middle /…/ path.




              • Capture the remainder
                /prefix/123-capture/…/*/…whatever…



                As said before, you don't often want too generic rewrite patterns. It does however make sense to combine static and specific comparisons with a .* sometimes.



                 RewriteRule ^(specific)/prefix/(d+)(/.*)?$ speci.php?id=$2&otherparams=$2


                This optionalized any /…/…/… trailing path segments. Which then of course requires the handling script to split them up, and variabl-ify extracted parameters
                itself (which is what Web-"MVC" frameworks do).




              • Trailing file "extensions"
                /old/path.HTML



                URLs don't really have file extensions. Which is what this entire reference is about (= URLs are virtual locators, not necessarily a direct filesystem image).
                However if you had a 1:1 file mapping before, you can craft simpler rules:



                 RewriteRule ^styles/([w.-]+).css$ sass-cache.php?old_fn_base=$1
                RewriteRule ^images/([w.-]+).gif$ png-converter.php?load_from=$2


                Other common uses are remapping obsolete .html paths to newer .php handlers, or just aliasing directory names only for individual (actual/real) files.




              • Ping-Pong (redirects and rewrites in unison)
                /ugly.html ←→ /pretty



                So at some point you're rewriting your HTML pages to carry only pretty links, as outlined by deceze.
                Meanwhile you'll still receive requests for the old paths, sometimes even from bookmarks. As workaround, you can ping-pong browsers to display/establish
                the new URLs.



                This common trick involves sending a 30x/Location redirect whenever an incoming URL follows the obsolete/ugly naming scheme.
                Browsers will then rerequest the new/pretty URL, which afterwards is rewritten (just internally) to the original or new location.



                 # redirect browser for old/ugly incoming paths
                RewriteRule ^old/teams.html$ /teams [R=301,QSA,END]

                # internally remap already-pretty incoming request
                RewriteRule ^teams$ teams.php [QSA,END]


                Note how this example just uses [END] instead of [L] to safely alternate. For older Apache 2.2 versions you can use other workarounds, besides also remapping
                query string parameters for example:
                Redirect ugly to pretty URL, remap back to the ugly path, without infinite loops




              • Spaces in patterns
                /this+that+



                It's not that pretty in browser address bars, but you can use spaces in URLs. For rewrite patterns use backslash-escaped spaces.
                Else just "-quote the whole pattern or substitution:



                 RewriteRule "^this [w ]+/(.*)$" "index.php?id=$1" [L]


                Clients serialize URLs with + or %20 for spaces. Yet in RewriteRules they're interpreted with literal characters for all relative path segments.



              Frequent duplicates:




              • Catch-all for a central dispatcher / front-controller script



                 RewriteCond %REQUEST_URI !-f
                RewriteCond %REQUEST_URI !-d
                RewriteRule ^.*$ index.php [L]


                Which is often used by PHP frameworks or WebCMS / portal scripts. The actual path splitting then is handled in PHP using $_SERVER["REQUEST_URI"]. So conceptionally it's pretty much the opposite of URL handling "per mod_rewrite". (Just use FallBackResource instead.)




              • Remove www. from hostname



                Note that this doesn't copy a query string along, etc.



                 # ┌──────────┐
                RewriteCond %HTTP_HOST ^www.(.+)$ [NC] │
                RewriteRule ^(.*)$ http://%1/$1 [R=301,L] │
                # ↓ └───┼────────────┘
                # └───────────────┘


                See also:

                · URL rewriting for different protocols in .htaccess

                · Generic htaccess redirect www to non-www

                · .htaccess - how to force "www." in a generic way?



                Note that RewriteCond/RewriteRule combos can be more complex, with matches (%1 and $1) interacting in both directions even:



                References %1 and $2, %3 between RewriteRule and RewriteCond
                Apache manual - mod_rewrite intro, Copyright 2015 The Apache Software Foundation, AL-2.0




              • Redirect to HTTPS://



                 RewriteCond %SERVER_PORT 80
                RewriteRule ^(.*)$ https://example.com/$1 [R,L]


                See also: https://wiki.apache.org/httpd/RewriteHTTPToHTTPS




              • "Removing" the PHP extension



                 RewriteCond %REQUEST_FILENAME.php -f
                RewriteRule ^(.+)$ $1.php [L] # or [END]


                See also: Removing the .php extension with mod_rewrite




              • Aliasing old .html paths to .php scripts



                See: http://httpd.apache.org/docs/2.4/rewrite/remapping.html#backward-compatibility




              • Rewrite from URL like "/page" to a script such as "/index.php/page"



                See mod_rewrite, php and the .htaccess file




              • Redirect subdomain to a folder



                See How can i get my htaccess to work (subdomains)?



              Prevalent .htaccess pitfalls



              Now take this with a grain of salt. Not every advise can be generalized to all contexts.
              This is just a simple summary of well-known and a few unobvious stumbling blocks:




              • Enable mod_rewrite and .htaccess



                To actually use RewriteRules in per-directory configuration files you must:



                • Check that your server has AllowOverride All enabled. Otherwise your per-directory .htaccess directives will go ignored, and RewriteRules won't work.


                • Obviously have mod_rewrite enabled in your httpd.conf modules section.


                • Prepend each list of rules with RewriteEngine On still. While mod_rewrite is implicitly active in <VirtualHost> and <Directory> sections,
                  the per-directory .htaccess files need it individually summoned.




              • The leading slash ^/ won't match



                You shouldn't start your .htaccess RewriteRule patterns with ^/ normally:



                 RewriteRule ^/article/d+$ …



                This is often seen in old tutorials. And it used to be correct for ancient Apache 1.x versions. Nowadays request paths are conveniently fully directory-relative in .htaccess RewriteRules. Just leave the leading / out.



                · Note that the leading slash is still correct in <VirtualHost> sections though. Which is why you often see it ^/? optionalized for rule parity.

                · Or when using a RewriteCond %REQUEST_URI you'd still match for a leading /.

                · See also Webmaster.SE: When is the leading slash (/) needed in mod_rewrite patterns?





              • <IfModule *> wrappers begone!



                You've probably seen this in many examples:



                <IfModule mod_rewrite.c>
                Rewrite…
                </IfModule>


                • It does make sense in <VirtualHost> sections - if it was combined with another fallback option, such as ScriptAliasMatch. (But nobody ever does that).

                • And it's commonly distributed for default .htaccess rulesets with many open source projects. There it's just meant as fallback, and keeps "ugly" URLs work as default.

                However you don't want that usually in your own .htaccess files.



                • Firstly, mod_rewrite does not randomly disengage. (If it did, you'd have bigger problems).

                • Were it really be disabled, your RewriteRules still wouldn't work anyway.

                • It's meant to prevent HTTP 500 errors. What it usually accomplishes is gracing your users with HTTP 404 errors instead. (Not so much more user-friendly if you think about it.)

                • Practically it just suppresses the more useful log entries, or server notification mails. You'd be none the wiser as to why your RewriteRules never work.


                What seems enticing as generalized safeguard, often turns out to be an obstacle in practice.





              • Don't use RewriteBase unless needed



                Many copy+paste examples contain a RewriteBase / directive. Which happens to be the implicit default anyway. So you don't actually need this. It's a workaround for fancy VirtualHost rewriting schemes, and misguessed DOCUMENT_ROOT paths for some shared hosters.



                It makes sense to use with individual web applications in deeper subdirectories. It can shorten RewriteRule patterns in such cases. Generally it's best to prefer relative path specifiers in per-directory rule sets.



                See also How does RewriteBase work in .htaccess




              • Disable MultiViews when virtual paths overlap



                URL rewriting is primarily used for supporting virtual incoming paths. Commonly you just have one dispatcher script (index.php) or a few individual handlers (articles.php, blog.php, wiki.php, …). The latter might clash with similar virtual RewriteRule paths.



                A request for /article/123 for example could map to article.php with a /123 PATH_INFO implicitly. You'd either have to guard your rules then with the commonplace RewriteCond !-f+!-d, and/or disable PATH_INFO support, or perhaps just disable Options -MultiViews.



                Which is not to say you always have to. Content-Negotiation is just an automatism to virtual resources.




              • Ordering is important



                See Everything you ever wanted to know about mod_rewrite
                if you haven't already. Combining multiple RewriteRules often leads to interaction. This isn't something to prevent habitually per [L] flag, but a scheme you'll embrace once versed.
                You can re-re-rewrite virtual paths from one rule to another, until it reaches an actual target handler.



                Still you'd often want to have the most specific rules (fixed string /forum/… patterns, or more restrictive placeholders [^/.]+) in the early rules.
                Generic slurp-all rules (.*) are better left to the later ones. (An exception is a RewriteCond -f/-d guard as primary block.)




              • Stylesheets and images stop working



                When you introduce virtual directory structures /blog/article/123 this impacts relative resource references in HTML (such as <img src=mouse.png>).
                Which can be solved by:



                • Only using server-absolute references href="/old.html" or src="/logo.png"

                • Often simply by adding <base href="/index"> into your HTML <head> section.
                  This implicitly rebinds relative references to what they were before.

                You could alternatively craft further RewriteRules to rebind .css or .png paths to their original locations.
                But that's both unneeded, or incurs extra redirects and hampers caching.



                See also: CSS, JS and images do not display with pretty url




              • RewriteConds just mask one RewriteRule



                A common misinterpetation is that a RewriteCond blocks multiple RewriteRules (because they're visually arranged together):



                 RewriteCond %SERVER_NAME localhost
                RewriteRule ^secret admin/tools.php
                RewriteRule ^hidden sqladmin.cgi


                Which it doesn't per default. You can chain them using the [S=2] flag. Else you'll have to repeat them. While sometimes you can craft an "inverted" primary rule to [END] the rewrite processing early.




              • QUERY_STRING exempt from RewriteRules



                You can't match RewriteRule index.php?x=y, because mod_rewrite compares just against relative paths per default. You can match them separately however via:



                 RewriteCond %QUERY_STRING b(?:param)=([^&]+)(?:&|$)
                RewriteRule ^add/(.+)$ add/%1/$1 # ←──﹪₁──┘


                See also How can I match query string variables with mod_rewrite?





              • .htaccess vs. <VirtualHost>



                If you're using RewriteRules in a per-directory config file, then worrying about regex performance is pointless. Apache retains
                compiled PCRE patterns longer than a PHP process with a common routing framework. For high-traffic sites you should however consider
                moving rulesets into the vhost server configuration, once they've been battle-tested.



                In this case, prefer the optionalized ^/? directory separator prefix. This allows to move RewriteRules freely between PerDir and server
                config files.




              • Whenever something doesn't work



                Fret not.




                • Compare access.log and error.log



                  Often you can figure out how a RewriteRule misbehaves just from looking at your error.log and access.log.
                  Correlate access times to see which request path originally came in, and which path/file Apache couldn't resolve to (error 404/500).



                  This doesn't tell you which RewriteRule is the culprit. But inaccessible final paths like /docroot/21-.itle?index.php may give away where to inspect further.
                  Otherwise disable rules until you get some predictable paths.




                • Enable the RewriteLog



                  See Apache RewriteLog docs. For debugging you can enable it in the vhost sections:



                  # Apache 2.2
                  RewriteLogLevel 5
                  RewriteLog /tmp/rewrite.log

                  # Apache 2.4
                  LogLevel alert rewrite:trace5
                  #ErrorLog /tmp/rewrite.log


                  That yields a detailed summary of how incoming request paths get modified by each rule:



                  [..] applying pattern '^test_.*$' to uri 'index.php'
                  [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
                  [..] applying pattern '^index.php$' to uri 'index.php'


                  Which helps to narrow down overly generic rules and regex mishaps.



                  See also:

                  · .htaccess not working (mod_rewrite)

                  · Tips for debugging .htaccess rewrite rules




                • Before asking your own question



                  As you might know, Stack Overflow is very suitable for asking questions on mod_rewrite. Make them on-topic
                  by including prior research and attempts (avoid redundant answers), demonstrate basic regex understanding, and:



                  • Include full examples of input URLs, falsly rewritten target paths, your real directory structure.

                  • The complete RewriteRule set, but also single out the presumed defective one.

                  • Apache and PHP versions, OS type, filesystem, DOCUMENT_ROOT, and PHPs $_SERVER environment if it's about a parameter mismatch.

                  • An excerpt from your access.log and error.log to verify what the existing rules resolved to. Better yet, a rewrite.log summary.

                  This nets quicker and more exact answers, and makes them more useful to others.





              • Comment your .htaccess



                If you copy examples from somewhere, take care to include a # comment and origin link. While it's merely bad manners to omit attribution,
                it often really hurts maintenance later. Document any code or tutorial source. In particular while unversed you should be
                all the more interested in not treating them like magic blackboxes.




              • It's not "SEO"-URLs



                Disclaimer: Just a pet peeve. You often hear pretty URL rewriting schemes referred to as "SEO" links or something. While this is useful for googling examples, it's a dated misnomer.



                None of the modern search engines are really disturbed by .html and .php in path segments, or ?id=123 query strings for that matter. Search engines of old, such as AltaVista, did avoid crawling websites with potentially ambigious access paths. Modern crawlers are often even craving for deep web resources.



                What "pretty" URLs should conceptionally be used for is making websites user-friendly.



                1. Having readable and obvious resource schemes.

                2. Ensuring URLs are long-lived (AKA permalinks).

                3. Providing discoverability through /common/tree/nesting.

                However don't sacrifice unique requirements for conformism.



              Tools



              There are various online tools to generate RewriteRules for most GET-parameterish URLs:



              • http://www.generateit.net/mod-rewrite/index.php

              • http://www.ipdistance.com/mod_rewrite.php

              • http://webtools.live2support.com/misc_rewrite.php

              Mostly just output [^/]+ generic placeholders, but likely suffices for trivial sites.







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited May 23 '17 at 12:10









              Community

              11




              11










              answered Jul 7 '15 at 22:00









              mariomario

              126k17184256




              126k17184256












              • Still needs a bit rewriting, more links, and the many subheadlines are somewhat obnoxious. There's some overlap with the other answers here, so can be cut down perhaps. It's mainly about the visual examples though, and that list of common gotchas.

                – mario
                Jul 7 '15 at 22:07






              • 2





                Didn't saw such a beauty of an answer for a long time! My eyes are glowing while I'm reading it. Please don't stop posting such answers :)

                – Rizier123
                Jul 8 '15 at 18:56











              • Excellent post. Made me understand the basic concepts of mod_rewrite very quickly!

                – breez
                Dec 9 '16 at 11:20

















              • Still needs a bit rewriting, more links, and the many subheadlines are somewhat obnoxious. There's some overlap with the other answers here, so can be cut down perhaps. It's mainly about the visual examples though, and that list of common gotchas.

                – mario
                Jul 7 '15 at 22:07






              • 2





                Didn't saw such a beauty of an answer for a long time! My eyes are glowing while I'm reading it. Please don't stop posting such answers :)

                – Rizier123
                Jul 8 '15 at 18:56











              • Excellent post. Made me understand the basic concepts of mod_rewrite very quickly!

                – breez
                Dec 9 '16 at 11:20
















              Still needs a bit rewriting, more links, and the many subheadlines are somewhat obnoxious. There's some overlap with the other answers here, so can be cut down perhaps. It's mainly about the visual examples though, and that list of common gotchas.

              – mario
              Jul 7 '15 at 22:07





              Still needs a bit rewriting, more links, and the many subheadlines are somewhat obnoxious. There's some overlap with the other answers here, so can be cut down perhaps. It's mainly about the visual examples though, and that list of common gotchas.

              – mario
              Jul 7 '15 at 22:07




              2




              2





              Didn't saw such a beauty of an answer for a long time! My eyes are glowing while I'm reading it. Please don't stop posting such answers :)

              – Rizier123
              Jul 8 '15 at 18:56





              Didn't saw such a beauty of an answer for a long time! My eyes are glowing while I'm reading it. Please don't stop posting such answers :)

              – Rizier123
              Jul 8 '15 at 18:56













              Excellent post. Made me understand the basic concepts of mod_rewrite very quickly!

              – breez
              Dec 9 '16 at 11:20





              Excellent post. Made me understand the basic concepts of mod_rewrite very quickly!

              – breez
              Dec 9 '16 at 11:20











              5














              Alternatives to mod_rewrite



              Many basic virtual URL schemes can be achieved without using RewriteRules. Apache allows PHP scripts to be invoked without .php extension, and with a virtual PATH_INFO argument.




              1. Use the PATH_INFO, Luke



                Nowadays AcceptPathInfo On is often enabled by default. Which basically allows .php and other resource URLs to carry a virtual argument:



                http://example.com/script.php/virtual/path


                Now this /virtual/path shows up in PHP as $_SERVER["PATH_INFO"] where you can handle any extra arguments however you like.



                This isn't as convenient as having Apache separate input path segments into $1, $2, $3 and passing them as distinct $_GET variables to PHP. It's merely emulating "pretty URLs" with less configuration effort.




              2. Enable MultiViews to hide the .php extension



                The simplest option to also eschew .php "file extensions" in URLs is enabling:



                Options +MultiViews


                This has Apache select article.php for HTTP requests on /article due to the matching basename. And this works well together with the aforementioned PATH_INFO feature. So you can just use URLs like http://example.com/article/virtual/title. Which makes sense if you have a traditional web application with multiple PHP invocation points/scripts.



                Note that MultiViews has a different/broader purpose though. It incurs a very minor performance penalty, because Apache always looks for other files with matching basenames. It's actually meant for Content-Negotiation, so browsers receive the best alternative among available resources (such as article.en.php, article.fr.php, article.jp.mp4).




              3. SetType or SetHandler for extensionless .php scripts



                A more directed approach to avoid carrying around .php suffixes in URLs is configuring the PHP handler for other file schemes. The simplest option is overriding the default MIME/handler type via .htaccess:



                DefaultType application/x-httpd-php


                This way you could just rename your article.php script to just article (without extension), but still have it processed as PHP script.



                Now this can have some security and performance implications, because all extensionless files would be piped through PHP now. Therefore you can alternatively set this behaviour for individual files only:



                <Files article>
                SetHandler application/x-httpd-php
                # or SetType
                </Files>


                This is somewhat dependent on your server setup and the used PHP SAPI. Common alternatives include ForceType application/x-httpd-php or AddHandler php5-script.




                Again take note that such settings propagate from one .htaccess to subfolders. You always should disable script execution (SetHandler None and Options -Exec or php_flag engine off etc.) for static resources, and upload/ directories etc.





              4. Other Apache rewriting schemes



                Among its many options, Apache provides mod_alias features - which sometimes work just as well as mod_rewrites RewriteRules. Note that most of those must be set up in a <VirtualHost> section however, not in per-directory .htaccess config files.



                • ScriptAliasMatch is primarily for CGI scripts, but also ought to works for PHP. It allows regexps just like any RewriteRule. In fact it's perhaps the most robust option to configurate a catch-all front controller.


                • And a plain Alias helps with a few simple rewriting schemes as well.


                • Even a plain ErrorDocument directive could be used to let a PHP script handle virtual paths. Note that this is a kludgy workaround however, prohibits anything but GET requests, and floods the error.log by definition.


                See http://httpd.apache.org/docs/2.2/urlmapping.html for further tips.







              share|improve this answer





























                5














                Alternatives to mod_rewrite



                Many basic virtual URL schemes can be achieved without using RewriteRules. Apache allows PHP scripts to be invoked without .php extension, and with a virtual PATH_INFO argument.




                1. Use the PATH_INFO, Luke



                  Nowadays AcceptPathInfo On is often enabled by default. Which basically allows .php and other resource URLs to carry a virtual argument:



                  http://example.com/script.php/virtual/path


                  Now this /virtual/path shows up in PHP as $_SERVER["PATH_INFO"] where you can handle any extra arguments however you like.



                  This isn't as convenient as having Apache separate input path segments into $1, $2, $3 and passing them as distinct $_GET variables to PHP. It's merely emulating "pretty URLs" with less configuration effort.




                2. Enable MultiViews to hide the .php extension



                  The simplest option to also eschew .php "file extensions" in URLs is enabling:



                  Options +MultiViews


                  This has Apache select article.php for HTTP requests on /article due to the matching basename. And this works well together with the aforementioned PATH_INFO feature. So you can just use URLs like http://example.com/article/virtual/title. Which makes sense if you have a traditional web application with multiple PHP invocation points/scripts.



                  Note that MultiViews has a different/broader purpose though. It incurs a very minor performance penalty, because Apache always looks for other files with matching basenames. It's actually meant for Content-Negotiation, so browsers receive the best alternative among available resources (such as article.en.php, article.fr.php, article.jp.mp4).




                3. SetType or SetHandler for extensionless .php scripts



                  A more directed approach to avoid carrying around .php suffixes in URLs is configuring the PHP handler for other file schemes. The simplest option is overriding the default MIME/handler type via .htaccess:



                  DefaultType application/x-httpd-php


                  This way you could just rename your article.php script to just article (without extension), but still have it processed as PHP script.



                  Now this can have some security and performance implications, because all extensionless files would be piped through PHP now. Therefore you can alternatively set this behaviour for individual files only:



                  <Files article>
                  SetHandler application/x-httpd-php
                  # or SetType
                  </Files>


                  This is somewhat dependent on your server setup and the used PHP SAPI. Common alternatives include ForceType application/x-httpd-php or AddHandler php5-script.




                  Again take note that such settings propagate from one .htaccess to subfolders. You always should disable script execution (SetHandler None and Options -Exec or php_flag engine off etc.) for static resources, and upload/ directories etc.





                4. Other Apache rewriting schemes



                  Among its many options, Apache provides mod_alias features - which sometimes work just as well as mod_rewrites RewriteRules. Note that most of those must be set up in a <VirtualHost> section however, not in per-directory .htaccess config files.



                  • ScriptAliasMatch is primarily for CGI scripts, but also ought to works for PHP. It allows regexps just like any RewriteRule. In fact it's perhaps the most robust option to configurate a catch-all front controller.


                  • And a plain Alias helps with a few simple rewriting schemes as well.


                  • Even a plain ErrorDocument directive could be used to let a PHP script handle virtual paths. Note that this is a kludgy workaround however, prohibits anything but GET requests, and floods the error.log by definition.


                  See http://httpd.apache.org/docs/2.2/urlmapping.html for further tips.







                share|improve this answer



























                  5












                  5








                  5







                  Alternatives to mod_rewrite



                  Many basic virtual URL schemes can be achieved without using RewriteRules. Apache allows PHP scripts to be invoked without .php extension, and with a virtual PATH_INFO argument.




                  1. Use the PATH_INFO, Luke



                    Nowadays AcceptPathInfo On is often enabled by default. Which basically allows .php and other resource URLs to carry a virtual argument:



                    http://example.com/script.php/virtual/path


                    Now this /virtual/path shows up in PHP as $_SERVER["PATH_INFO"] where you can handle any extra arguments however you like.



                    This isn't as convenient as having Apache separate input path segments into $1, $2, $3 and passing them as distinct $_GET variables to PHP. It's merely emulating "pretty URLs" with less configuration effort.




                  2. Enable MultiViews to hide the .php extension



                    The simplest option to also eschew .php "file extensions" in URLs is enabling:



                    Options +MultiViews


                    This has Apache select article.php for HTTP requests on /article due to the matching basename. And this works well together with the aforementioned PATH_INFO feature. So you can just use URLs like http://example.com/article/virtual/title. Which makes sense if you have a traditional web application with multiple PHP invocation points/scripts.



                    Note that MultiViews has a different/broader purpose though. It incurs a very minor performance penalty, because Apache always looks for other files with matching basenames. It's actually meant for Content-Negotiation, so browsers receive the best alternative among available resources (such as article.en.php, article.fr.php, article.jp.mp4).




                  3. SetType or SetHandler for extensionless .php scripts



                    A more directed approach to avoid carrying around .php suffixes in URLs is configuring the PHP handler for other file schemes. The simplest option is overriding the default MIME/handler type via .htaccess:



                    DefaultType application/x-httpd-php


                    This way you could just rename your article.php script to just article (without extension), but still have it processed as PHP script.



                    Now this can have some security and performance implications, because all extensionless files would be piped through PHP now. Therefore you can alternatively set this behaviour for individual files only:



                    <Files article>
                    SetHandler application/x-httpd-php
                    # or SetType
                    </Files>


                    This is somewhat dependent on your server setup and the used PHP SAPI. Common alternatives include ForceType application/x-httpd-php or AddHandler php5-script.




                    Again take note that such settings propagate from one .htaccess to subfolders. You always should disable script execution (SetHandler None and Options -Exec or php_flag engine off etc.) for static resources, and upload/ directories etc.





                  4. Other Apache rewriting schemes



                    Among its many options, Apache provides mod_alias features - which sometimes work just as well as mod_rewrites RewriteRules. Note that most of those must be set up in a <VirtualHost> section however, not in per-directory .htaccess config files.



                    • ScriptAliasMatch is primarily for CGI scripts, but also ought to works for PHP. It allows regexps just like any RewriteRule. In fact it's perhaps the most robust option to configurate a catch-all front controller.


                    • And a plain Alias helps with a few simple rewriting schemes as well.


                    • Even a plain ErrorDocument directive could be used to let a PHP script handle virtual paths. Note that this is a kludgy workaround however, prohibits anything but GET requests, and floods the error.log by definition.


                    See http://httpd.apache.org/docs/2.2/urlmapping.html for further tips.







                  share|improve this answer















                  Alternatives to mod_rewrite



                  Many basic virtual URL schemes can be achieved without using RewriteRules. Apache allows PHP scripts to be invoked without .php extension, and with a virtual PATH_INFO argument.




                  1. Use the PATH_INFO, Luke



                    Nowadays AcceptPathInfo On is often enabled by default. Which basically allows .php and other resource URLs to carry a virtual argument:



                    http://example.com/script.php/virtual/path


                    Now this /virtual/path shows up in PHP as $_SERVER["PATH_INFO"] where you can handle any extra arguments however you like.



                    This isn't as convenient as having Apache separate input path segments into $1, $2, $3 and passing them as distinct $_GET variables to PHP. It's merely emulating "pretty URLs" with less configuration effort.




                  2. Enable MultiViews to hide the .php extension



                    The simplest option to also eschew .php "file extensions" in URLs is enabling:



                    Options +MultiViews


                    This has Apache select article.php for HTTP requests on /article due to the matching basename. And this works well together with the aforementioned PATH_INFO feature. So you can just use URLs like http://example.com/article/virtual/title. Which makes sense if you have a traditional web application with multiple PHP invocation points/scripts.



                    Note that MultiViews has a different/broader purpose though. It incurs a very minor performance penalty, because Apache always looks for other files with matching basenames. It's actually meant for Content-Negotiation, so browsers receive the best alternative among available resources (such as article.en.php, article.fr.php, article.jp.mp4).




                  3. SetType or SetHandler for extensionless .php scripts



                    A more directed approach to avoid carrying around .php suffixes in URLs is configuring the PHP handler for other file schemes. The simplest option is overriding the default MIME/handler type via .htaccess:



                    DefaultType application/x-httpd-php


                    This way you could just rename your article.php script to just article (without extension), but still have it processed as PHP script.



                    Now this can have some security and performance implications, because all extensionless files would be piped through PHP now. Therefore you can alternatively set this behaviour for individual files only:



                    <Files article>
                    SetHandler application/x-httpd-php
                    # or SetType
                    </Files>


                    This is somewhat dependent on your server setup and the used PHP SAPI. Common alternatives include ForceType application/x-httpd-php or AddHandler php5-script.




                    Again take note that such settings propagate from one .htaccess to subfolders. You always should disable script execution (SetHandler None and Options -Exec or php_flag engine off etc.) for static resources, and upload/ directories etc.





                  4. Other Apache rewriting schemes



                    Among its many options, Apache provides mod_alias features - which sometimes work just as well as mod_rewrites RewriteRules. Note that most of those must be set up in a <VirtualHost> section however, not in per-directory .htaccess config files.



                    • ScriptAliasMatch is primarily for CGI scripts, but also ought to works for PHP. It allows regexps just like any RewriteRule. In fact it's perhaps the most robust option to configurate a catch-all front controller.


                    • And a plain Alias helps with a few simple rewriting schemes as well.


                    • Even a plain ErrorDocument directive could be used to let a PHP script handle virtual paths. Note that this is a kludgy workaround however, prohibits anything but GET requests, and floods the error.log by definition.


                    See http://httpd.apache.org/docs/2.2/urlmapping.html for further tips.








                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited May 23 '17 at 12:34









                  Community

                  11




                  11










                  answered Jul 5 '15 at 11:14









                  mariomario

                  126k17184256




                  126k17184256















                      protected by hjpotter92 Nov 17 '14 at 23:32



                      Thank you for your interest in this question.
                      Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).



                      Would you like to answer one of these unanswered questions instead?



                      Popular posts from this blog

                      Top Tejano songwriter Luis Silva dead of heart attack at 64

                      ReactJS Fetched API data displays live - need Data displayed static

                      Evgeni Malkin