Skip to content

Commit 7a53fdc

Browse files
authored
Merge pull request #18219 from github/repo-sync
repo sync
2 parents 5aac396 + 75c0ec5 commit 7a53fdc

File tree

5 files changed

+43
-17
lines changed

5 files changed

+43
-17
lines changed

middleware/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { setDefaultFastlySurrogateKey } from './set-fastly-surrogate-key.js'
1919
import setFastlyCacheHeaders from './set-fastly-cache-headers.js'
2020
import reqUtils from './req-utils.js'
2121
import recordRedirect from './record-redirect.js'
22-
import connectSlashes from 'connect-slashes'
2322
import handleErrors from './handle-errors.js'
2423
import handleInvalidPaths from './handle-invalid-paths.js'
2524
import handleNextDataPath from './handle-next-data-path.js'
@@ -67,6 +66,7 @@ import protect from './overload-protection.js'
6766
import fastHead from './fast-head.js'
6867
import fastlyCacheTest from './fastly-cache-test.js'
6968
import fastRootRedirect from './fast-root-redirect.js'
69+
import trailingSlashes from './trailing-slashes.js'
7070

7171
const { DEPLOYMENT_ENV, NODE_ENV } = process.env
7272
const isDevelopment = NODE_ENV === 'development'
@@ -263,7 +263,7 @@ export default function (app) {
263263

264264
// *** Redirects, 3xx responses ***
265265
// I ordered these by use frequency
266-
app.use(connectSlashes(false))
266+
app.use(instrument(trailingSlashes, './redirects/trailing-slashes'))
267267
app.use(instrument(redirectsExternal, './redirects/external'))
268268
app.use(instrument(languageCodeRedirects, './redirects/language-code-redirects')) // Must come before contextualizers
269269
app.use(instrument(handleRedirects, './redirects/handle-redirects')) // Must come before contextualizers

middleware/trailing-slashes.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { cacheControlFactory } from './cache-control.js'
2+
3+
const cacheControl = cacheControlFactory(60 * 60)
4+
5+
export default function trailingSlashes(req, res, next) {
6+
if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
7+
const split = req.url.split('?')
8+
let pathname = split.shift()
9+
if (pathname !== '/' && pathname.endsWith('/')) {
10+
while (pathname.endsWith('/')) {
11+
pathname = pathname.slice(0, pathname.length - 1)
12+
}
13+
let url = pathname
14+
if (split.length) {
15+
url += `?${split.join('?')}`
16+
}
17+
// So it can be cached in the CDN
18+
res.removeHeader('set-cookie')
19+
cacheControl(res)
20+
return res.redirect(301, url)
21+
}
22+
}
23+
24+
next()
25+
}

package-lock.json

Lines changed: 0 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
"cheerio": "^1.0.0-rc.10",
2222
"classnames": "^2.3.1",
2323
"connect-datadog": "0.0.9",
24-
"connect-slashes": "^1.4.0",
2524
"cookie-parser": "^1.4.6",
2625
"cors": "^2.8.5",
2726
"csurf": "^1.11.0",

tests/routing/redirects.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ describe('redirects', () => {
154154
expect(res.headers.location).toBe('/ja')
155155
expect(res.headers['cache-control']).toBe('private, no-store')
156156
})
157+
test('trailing slash on languaged homepage should permantently redirect', async () => {
158+
const res = await get('/en/')
159+
expect(res.statusCode).toBe(301)
160+
expect(res.headers.location).toBe('/en')
161+
expect(res.headers['set-cookie']).toBeUndefined()
162+
expect(res.headers['cache-control']).toContain('public')
163+
expect(res.headers['cache-control']).toMatch(/max-age=\d+/)
164+
})
165+
test('trailing slash with query string on languaged homepage should permantently redirect', async () => {
166+
const res = await get('/ja/?foo=bar&bar=foo')
167+
expect(res.statusCode).toBe(301)
168+
expect(res.headers.location).toBe('/ja?foo=bar&bar=foo')
169+
expect(res.headers['set-cookie']).toBeUndefined()
170+
expect(res.headers['cache-control']).toContain('public')
171+
expect(res.headers['cache-control']).toMatch(/max-age=\d+/)
172+
})
157173
})
158174

159175
describe('external redirects', () => {

0 commit comments

Comments
 (0)