# Understanding the Nginx location directive and redirects

The Nginx `location` directive chooses configuration based on the request URI.
You can use it to serve files, proxy requests, or return redirects.

## A basic redirect

For a small example of using a location block to serve files as plain text, see
[the NGINX text response guide](/display-any-file-in-a-browser-as-plain-text).

This server redirects one exact path:

```nginx
server {
  listen 80;
  server_name staging.example.com;
  root /var/www/staging.example.com/public_html;

  location = /old-page {
    return 308 https://www.example.com/new-page;
  }
}
```

`308` is a permanent redirect that preserves the request method and body. Use
`302` or `307` while testing if you do not want clients to cache the redirect.

## Location modifiers

The syntax is:

```nginx
location [ = | ~ | ~* | ^~ ] uri { ... }
```

| Modifier    | Match                                                 |
| ----------- | ----------------------------------------------------- |
| No modifier | URI prefix                                            |
| `=`         | Exact URI                                             |
| `~`         | Case-sensitive regular expression                     |
| `~*`        | Case-insensitive regular expression                   |
| `^~`        | URI prefix; skip regular expressions if this one wins |

Nginx matches against the normalized URI path, without the query string.

### How Nginx picks a location

For non-nested locations, the search is:

1. An exact `=` match wins immediately.
2. Nginx remembers the longest matching prefix.
3. If that prefix uses `^~`, it wins immediately.
4. Otherwise, Nginx checks regular expressions in file order. The first match
   wins.
5. If no regular expression matches, the remembered prefix wins.

Prefix order does not matter. Regular expression order does.

### Prefix matches

A location without a modifier matches from the start of the URI:

```nginx
location / {
  # Matches every URI
}

location /foo {
  # Matches /foo, /foo/bar, and /foobar
  # Does not match /bar/foo
}
```

Use a trailing slash when you mean a path segment:

```nginx
location /foo/ {
  # Matches /foo/bar, but not /foobar
}
```

Prefix matching is case-sensitive on Linux, so `/foo` does not match `/FOO`.
Nginx ignores ASCII case for prefix locations on macOS and Cygwin.

### Exact matches

Use `=` when only one URI should match:

```nginx
location = / {
  # Matches only /
}

location = /foo {
  # Matches only /foo
}
```

## Regular expression matches

Nginx uses PCRE-compatible regular expressions. `~` is case-sensitive; `~*` is
case-insensitive.

This redirects an old tag URL and keeps its query string:

```nginx
location ~ ^/tag/(?<slug>[^/]+)$ {
  return 308 /tags/$slug$is_args$args;
}
```

For `/tag/nginx?source=old`:

- `^` anchors the match to the start of the URI
- `(?<slug>[^/]+)` captures one path segment as `$slug`
- `$` anchors the match to the end
- `$is_args` becomes `?` when arguments exist, or an empty string otherwise
- `$args` contains the query string

The result is `/tags/nginx?source=old` without needing an `if` block. See the
[complete list of Nginx variables](https://nginx.org/en/docs/varindex.html).

## More regular expression examples

### Cache versioned static files

Long cache lifetimes work best for files with content hashes in their names:

```nginx
location ~* \.(?:avif|css|gif|ico|jpe?g|js|mjs|otf|png|svg|ttf|webp|woff2?)$ {
  add_header Cache-Control "public, max-age=31536000, immutable";
  access_log off;
}
```

The escaped `\.` matches a literal dot. The `~*` makes file extensions
case-insensitive.

### Keep favicon requests out of the logs

Put this before a broader regular expression location, because the first
matching regular expression wins:

```nginx
location ~* ^/(?:favicon[^/]*\.(?:ico|png|svg)|apple-touch-icon[^/]*\.png)$ {
  log_not_found off;
  access_log off;
}
```

## Test redirects with curl

Browsers cache permanent redirects aggressively. Test with `curl` while tuning
the configuration:

```shell
$ curl --silent --show-error --head \
    'http://staging.example.com/tag/nginx?source=old'
HTTP/1.1 308 Permanent Redirect
Server: nginx
Location: http://staging.example.com/tags/nginx?source=old
```

`--head` (`-I`) sends a `HEAD` request, so the server returns headers without a
response body. It is fast and usually enough for testing Nginx redirects.

Some applications reject `HEAD` or handle it differently from `GET`. To test a
normal `GET`, print its headers and discard its body:

```shell
curl --silent --show-error --dump-header - --output /dev/null \
  'http://staging.example.com/tag/nginx?source=old'
```

This still downloads the response body. It simply does not save or print it.

To print only the destination:

```shell
$ curl --silent --show-error --head \
    'http://staging.example.com/tag/nginx?source=old' \
  | grep -i '^location:'
Location: http://staging.example.com/tags/nginx?source=old
```

Add `--location` to follow the full redirect chain.

For more complex URI changes, see Nginx's
[`rewrite` directive](https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite).
