# URL rewriting

> Source: https://ferron.sh/docs/use-cases/traffic/url-rewriting

URL rewriting is useful when your application expects "pretty URLs" that map to a single entry script (common in PHP CMS/framework stacks). It is also useful when you need to preserve old URL structures after migrations.

Ferron applies rewrites in the request pipeline, before proxying or static file serving, so later stages use the rewritten URL. Rewrites do not trigger a new round of `location` matching. Ferron selects the `location` block once, on the original URL. The client sees no redirect, meaning the rewrite is transparent.

For many applications behind reverse proxy, rewriting is not required. Those apps usually handle routing themselves, and Ferron only forwards requests with `proxy` (often using `location` blocks).

> [!tip]
> Use `rewrite_log true` while debugging to verify which rules match. Ferron logs each rewrite operation to the error log.

> [!info]
> For directive reference, see [Configuration: URL rewriting](https://ferron.sh/docs/configuration/routing/rewrite.md).

## Single-page application fallback

A common pattern is rewriting unknown routes to `/` so client-side routing works:

```ferron
example.com {
    root /var/www/html
    rewrite r"^/.*" "/" {
        last
        directory false
        file false
    }
}
```

This preserves real files (for example `/assets/app.js`) while routing non-file paths (for example `/dashboard/settings`) to your SPA entry point.

## PHP front-controller pattern

Many PHP applications route requests through `index.php`:

```ferron
example.com {
    root /var/www/app/public
    rewrite r"^/(.*)" "/index.php/$1" {
        file false
        directory false
        last
    }
}
```

CMS/framework setups commonly use this pattern where the app resolves routes internally.

## Legacy URL migration

To keep old URLs working after restructuring paths:

```ferron
example.com {
    root /var/www/html
    rewrite r"^/old-path/(.*)" "/new-path/$1" {
        last
    }
    rewrite r"^/blog/([^/]+)/?(?:$|[?#])" "/blog.php?slug=$1" {
        last
    }
}
```

## Clean URLs without `.html` for static files

To serve clean URLs (without `.html`) for static files, you can use a regex pattern that matches any path except those ending with `.html`:

```ferron
example.com {
    root /var/www/html

    # Rewrite /test?a=b&c=d -> /test.html?a=b&c=d
    rewrite r"^/([^?#]*[^?#/])($|[?#].*)" "/$1.html$2" {
        file false
    }

    # Redirect /test/?a=b&c=d -> /test?a=b&c=d
    status 301 {
        regex r"^/([^?#]*[^?#/])/($|[?#].*)"
        location /$1$2
    }
}
```

This is useful when serving websites generated with a static site generator (SSG) such as Jekyll, Hugo, or Astro.

## Chained rules without `last`

Without `last true`, multiple rewrite rules can chain together:

```ferron
example.com {
    rewrite r"^/legacy/(.*)" "/modern/$1"
    rewrite r"^/modern/(.*)" "/current/$1"
}
```

The first rule rewrites a request to `/legacy/foo` to `/modern/foo`. Then the second rule rewrites it to `/current/foo`.