Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3182ac4
fix: _redircts to use /*/ to /:splat/ prefix
wackerow Dec 1, 2025
a9d0310
feat: add redirects for ext 404s
wackerow Dec 1, 2025
0965f70
fix(i18n): use named single-segment `/:locale` prefix
wackerow Dec 1, 2025
ecba9a4
patch: nesting redirects
wackerow Dec 2, 2025
3b68b3a
patch: no and ph handlers to top
wackerow Dec 2, 2025
0b528e8
patch: lowercase all incoming paths
wackerow Dec 2, 2025
a8829c3
refactor: use next.js middleware for all redirect logic
wackerow Dec 2, 2025
8e7ddd2
remove: _redirects
wackerow Dec 2, 2025
b061e60
patch: /content/ redirect trailing slash
wackerow Dec 3, 2025
6531cd7
refactor: pass optional type
wackerow Dec 3, 2025
568c9c8
refactor: use next.config redirects
wackerow Dec 3, 2025
923f76e
patch: redirect destinations
wackerow Dec 3, 2025
9f8dc51
patch: redundant string interpolation
wackerow Dec 3, 2025
ebc8208
refactor: variable naming
wackerow Dec 3, 2025
97f545d
refactor: move redirects to redirects.config.js
wackerow Dec 3, 2025
0d8c0e0
Merge pull request #16782 from ethereum/dev
corwintines Dec 3, 2025
e09fcff
Merge pull request #16754 from ethereum/hot-fix-redirects
wackerow Dec 3, 2025
2cd04af
feat: update fusaka upgrade details
wackerow Dec 3, 2025
20d16cd
Merge pull request #16785 from ethereum/fusaka-history-update
corwintines Dec 3, 2025
07ef70d
Merge branch 'master' into staging
wackerow Dec 3, 2025
79f6c87
improve FusakaBanner mobile layout with better spacing, hierarchy, an…
pettinarip Dec 3, 2025
ec5ad9e
Merge pull request #16787 from ethereum/patch-fusaka-banner-layout
corwintines Dec 4, 2025
32a487b
Merge pull request #16784 from ethereum/staging
wackerow Dec 4, 2025
cdadeb5
patch: fusaka history header
wackerow Dec 4, 2025
527ce9d
Merge pull request #16795 from ethereum/hot-fix-fusaka-label
corwintines Dec 4, 2025
7284cad
Merge pull request #16796 from ethereum/master
wackerow Dec 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import { DEFAULT_LOCALE } from "./src/lib/constants"
const handleI18nRouting = createMiddleware(routing)

export default function middleware(request: NextRequest) {
// Normalize to lowercase paths site-wide (URLs are case-insensitive by spec,
// but our routes are defined in lowercase). Do this BEFORE i18n routing.
const originalPath = request.nextUrl.pathname
const lowerPath = originalPath.toLowerCase()
if (originalPath !== lowerPath) {
const url = request.nextUrl.clone()
url.pathname = lowerPath
return NextResponse.redirect(url, 301)
}

// Handle i18n routing
const response = handleI18nRouting(request)

// Upgrade default-locale strip redirects from 307 to 301 for SEO
Expand Down
37 changes: 37 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ const createNextIntlPlugin = require("next-intl/plugin")

const { withSentryConfig } = require("@sentry/nextjs")

const redirects = require("./redirects.config")

const i18nConfigJson = require("./i18n.config.json")

const withNextIntl = createNextIntlPlugin()

const LIMIT_CPUS = Number(process.env.LIMIT_CPUS ?? 2)
Expand Down Expand Up @@ -135,6 +139,39 @@ module.exports = (phase, { defaultConfig }) => {
},
]
},
async redirects() {
// Build a strict locale matcher from configured locales
const LOCALE_ALTS = i18nConfigJson.map(({ code }) => code).join("|") // e.g. "en|es|fr|..."

// Helper function to generate both English (no prefix) and locale-prefixed redirects
const createRedirect = (source, destination, permanent = true) => {
// For external URLs, don't modify the destination
const isExternal = destination.startsWith("http")

// English / default-locale: no prefix in source or destination
const defaultRedirect = { source, destination, permanent }

// Locale-prefixed: only match allowed locales (prevents matching arbitrary segments)
const localeRedirect = {
source: `/:locale(${LOCALE_ALTS})${source}`,
destination: isExternal ? destination : `/:locale${destination}`,
permanent,
}

return [defaultRedirect, localeRedirect]
}

return [
// Custom locale aliases redirects
{ source: "/no/:path*", destination: "/nb/:path*", permanent: true },
{ source: "/ph/:path*", destination: "/fil/:path*", permanent: true },

// All primary redirects
...redirects.flatMap(([source, destination, permanent]) =>
createRedirect(source, destination, permanent)
),
]
},
}

nextConfig = {
Expand Down
190 changes: 0 additions & 190 deletions public/_redirects

This file was deleted.

2 changes: 1 addition & 1 deletion public/content/ethereum-forks/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Looking for future protocol upgrades? [Learn about upcoming upgrades on the Ethe

## 2025 {#2025}

### Fulu-Osaka ("Fusaka", _in progress_) {#fusaka}
### Fulu-Osaka ("Fusaka") {#fusaka}

<NetworkUpgradeSummary name="fusaka" />

Expand Down
104 changes: 104 additions & 0 deletions redirects.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// All primary redirects ([source, destination, permanent? (default true)])

/** @type { [string, string, boolean | undefined][] } */
module.exports = [
["/discord", "https://discord.gg/ethereum-org"],
["/writing-cohort", "https://ethereumwriterscohort.carrd.co/"],
["/pdfs/:path*", "/"],
["/brand", "/assets/"],
["/ethereum.html", "/what-is-ethereum/"],
["/ether", "/what-is-ether/"],
["/eth", "/what-is-ether/"],
["/token", "/developers/"],
["/crowdsale", "/developers/"],
["/cli", "/developers/"],
["/greeter", "/developers/"],
["/roadmap/vision", "/roadmap/"],
["/search", "/"],
["/garden", "/roadmap/"],
["/download", "/wallets/find-wallet/"],
["/how", "/guides/"],
["/content/:path*", "/:path*"],
["/nfts", "/nft/"],
["/daos", "/dao/"],
["/layer2", "/layer-2/"],
["/grants", "/community/grants/"],
["/java", "/developers/docs/programming-languages/java/"],
["/python", "/developers/docs/programming-languages/python/"],
["/javascript", "/developers/docs/programming-languages/javascript/"],
["/golang", "/developers/docs/programming-languages/golang/"],
["/rust", "/developers/docs/programming-languages/rust/"],
["/dot-net", "/developers/docs/programming-languages/dot-net/"],
["/delphi", "/developers/docs/programming-languages/delphi/"],
["/dart", "/developers/docs/programming-languages/dart/"],
["/languages", "/community/language-resources/"],
[
"/developers/docs/mining",
"/developers/docs/consensus-mechanisms/pow/mining/",
],
["/beginners", "/what-is-ethereum/"],
["/build", "/developers/learning-tools/"],
["/eth2/beacon-chain", "/roadmap/beacon-chain/"],
["/eth2/the-beacon-chain", "/roadmap/beacon-chain/"],
["/upgrades/the-beacon-chain", "/roadmap/beacon-chain/"],
["/eth2/merge", "/roadmap/merge/"],
["/eth2/the-merge", "/roadmap/merge/"],
["/upgrades/the-merge", "/roadmap/merge/"],
["/eth2/docking", "/roadmap/merge/"],
["/upgrades/docking", "/roadmap/merge/"],
["/eth2/the-docking", "/roadmap/merge/"],
["/upgrades/the-docking", "/roadmap/merge/"],
["/eth2/shard-chains", "/roadmap/danksharding/"],
["/upgrades/shard-chains", "/roadmap/danksharding/"],
["/upgrades/sharding", "/roadmap/danksharding/"],
["/upgrades/merge", "/roadmap/merge/"],
["/upgrades/merge/issuance", "/roadmap/merge/issuance"],
["/upgrades/beacon-chain", "/roadmap/beacon-chain"],
["/upgrades/vision", "/roadmap/"],
["/upgrades", "/roadmap"],
["/upgrades/get-involved", "/contributing"],
["/eth2/staking", "/staking/"],
["/eth2/vision", "/roadmap/vision/"],
["/eth2/get-involved", "/contributing/"],
["/eth2/get-involved/bug-bounty", "/bug-bounty/"],
["/upgrades/get-involved/bug-bounty", "/bug-bounty/"],
["/eth2/deposit-contract", "/staking/deposit-contract/"],
["/eth2", "/roadmap/"],
["/developers/docs/scaling/layer-2-rollups", "/developers/docs/scaling"],
["/developers/docs/layer-2-scaling", "/layer-2/"],
["/about/web-developer", "/about/#open-jobs"],
["/about/product-designer", "/about/#open-jobs"],
["/use", "/apps/"],
["/dapps", "/apps/"],
[
"/contributing/translation-program/translation-guide",
"/contributing/translation-program/faq/",
],
[
"/contributing/translation-program/content-versions",
"/contributing/translation-program/",
],
[
"/contributing/translation-program/content-buckets",
"/contributing/translation-program/",
],
[
"/developers/docs/smart-contracts/source-code-verification",
"/developers/docs/smart-contracts/verifying/",
],
[
"/developers/docs/smart-contracts/upgrading-smart-contracts",
"/developers/docs/smart-contracts/upgrading/",
],
["/staking/withdraws", "/staking/withdrawals/"],
[
"/guides/how-to-register-an-ethereum-account",
"/guides/how-to-create-an-ethereum-account/",
],
["/deprecated-software", "/apps/"],
["/enterprise/private-ethereum", "/enterprise/"],
["/dashboards", "/resources"],
["/tds", "/trillion-dollar-security"],
["/10-years", "/10years"],
["/history", "/ethereum-forks"],
]
Loading
Loading