Back to blog

Redirects Died on Deploy Day. Bitrix Didn't Warn Us.

We went headless in February. In April, our SEO consultant sent a spreadsheet: 340 broken links. Three years of redirect rules, gone the day we deployed Next.js. No errors in the logs. Next.js had no idea these rules were supposed to exist in the first place.

Bitrix stores redirect rules inside PHP. When the frontend moves to a separate process, those rules stay there — and they stop working silently.

How Bitrix handles redirects

Bitrix stores redirect rules in two separate places, and neither of them is visible to Next.js.

The first is URLREWRITE.PHP in the site root — a PHP file with a flat array of rules: [CONDITION] maps to [PATH] and [QUERY]. It hooks into every request via bitrix/.settings.php. When Bitrix was rendering HTML itself, this worked fine. When Next.js renders the page, URLREWRITE.PHP doesn't get called at all.

The second is the b_urlrewrite database table. The SEO module writes here: redirects for old product URLs, renamed category paths, manual rules from the admin panel. Same outcome — Next.js doesn't read this table.

340 rules accumulated over three years disappeared from users and GoogleBot on the day we deployed Next.js.

Three approaches, three trade-offs

We looked at three options. Each has a cost.

The first is putting everything into middleware.ts. Rules live at the Edge, 301 responses go out before any rendering happens. Clean in theory. In practice it breaks within two weeks when a content manager adds a redirect in Bitrix admin and assumes it just works — not knowing they also need to sync the file.

The second is an Nginx map file. Nginx reads a flat rules file and issues return 301 before requests reach Next.js. Fast, no JavaScript involved at all. But the file has to regenerate whenever Bitrix changes: a script hits b_urlrewrite, builds the map, runs nginx -s reload. That's basically a CI/CD pipeline for redirects. Not a joke.

The third is a hybrid plus a 404 catch-all. Nginx handles static rules from b_urlrewrite (the ones that change once a quarter). Next.js middleware handles dynamic ones — SEO filter rewrites, parametric URLs. And for everything else: app/[...not-found]/page.tsx, which on a 404 asks Bitrix: "is there a redirect for this URL?"

Bitrix REST API doesn't expose the redirect table natively — that's the first thing you discover. You need a custom PHP handler. We wrote a method in local/php_interface/iblockHandlers.php that takes a URL and returns the matching rule from b_urlrewrite or null.

Latency on the catch-all is a real concern. Every 404 hits the backend. At 50k sessions/day, that shows up in p95. Solution: Redis with a 24-hour TTL. A real 404 is fast. A found redirect is fast. A confirmed "no redirect here" gets cached under its own key.

What we actually chose

The hybrid. Nginx handles 280 of the 340 rules — the static ones: old product URLs after a rebrand, renamed categories. They change once a quarter, and a cron job regenerates the map.

The remaining 60 — SEO filter dynamics and parametric URLs — go into Next.js middleware. Middleware reads a JSON file generated by the same cron task.

The catch-all handles the edge cases we missed. In the first three months it caught 22 redirects that weren't in the initial export.

The operational question nobody asks before migration

When you move Bitrix to headless, redirect management ownership shifts from Bitrix admin to your deployment pipeline — and most teams don't decide this before go-live.

Who manages redirect rules now?

Before, a content manager went to Bitrix admin, added a rule, clicked Save, and it worked. Now it works only if the rule makes it from the database into the cron script, then into the Nginx map or middleware.json, then onto the server.

Our answer: content managers keep working in Bitrix admin. The sync script runs on cron every 6 hours. There's a "sync now" button in our internal tooling for urgent cases. The 6-hour delay annoyed everyone at first. After we explained the alternative — CI/CD on every change — we agreed it was fine.

Three things we got wrong

URLREWRITE.PHP and the b_urlrewrite table aren't the same thing. We had 40 rules only in the file, never written to the database. They disappeared first and were found last.

Bitrix's SEO filter generates canonical URLs and redirects dynamically when filter parameters change. They can't be exported statically because they're built at request time. Nginx maps don't cover them. They need Next.js middleware with URL-parsing logic specific to Bitrix's filter format.

Testing redirect flows in Chrome isn't enough. Some Bitrix rules fired only for specific User-Agent strings. Everything looked fine in the browser. GoogleBot saw 404s.

The short version

Headless doesn't break redirects. It just stops executing them, because they lived where Next.js doesn't look.

Audit your redirects before going headless, not two months after. Export b_urlrewrite. Check URLREWRITE.PHP. Count the rules. More than a hundred — budget a week for sync architecture.

340 broken links. Three years of SEO work. Back in four days. Only because we knew where to look.


*Building a headless Bitrix + Next.js project? See also: Five things Bitrix REST API doesn't tell you before you go headless and What we kept on Bitrix when going headless.*