Using page aliases combined with the `indexify` leads to redirect loops
Normally, page aliases in Antora are used to resolve outdated links without causing issues. However, with indexify
, the same URL may inadvertently be used for both the source and target of a redirect, leading to loops. For example, creating a page alias for modules/manage/security/authorization.adoc
to point to modules/manage/security/authorization/index.adoc
can lead to a redirect loop where manage/security/authorization/
points to manage/security/authorization/
.
Currently, Antora does not seem to check if the alias results in a URL that is identical to the target page’s URL.
Proposed Solution:
Update methods in produceRedirects()
to skip aliases that lead to redirect loops:
function isRedirectLoop (file) {
if (file.pub.url === file.rel.pub.url) return true
return false
}
function createHttpdHtaccess (files, urlPath, directoryRedirects = false) {
const rules = files.reduce((accum, file) => {
if (isRedirectLoop(file)) return accum
delete file.out
let fromUrl = file.pub.url
fromUrl = ~fromUrl.indexOf('%20') ? `'${urlPath}${fromUrl.replace(ENCODED_SPACE_RX, ' ')}'` : urlPath + fromUrl
let toUrl = file.rel.pub.url
toUrl = ~toUrl.indexOf('%20') ? `'${urlPath}${toUrl.replace(ENCODED_SPACE_RX, ' ')}'` : urlPath + toUrl
// see https://httpd.apache.org/docs/current/en/mod/mod_alias.html#redirect
// NOTE: redirect rule for directory prefix does not require trailing slash
if (file.pub.splat) {
accum.push(`Redirect 302 ${fromUrl} ${stripTrailingSlash(toUrl)}`)
} else if (directoryRedirects) {
accum.push(`RedirectMatch 301 ^${regexpEscape(fromUrl)}$ ${stripTrailingSlash(toUrl)}`)
} else {
accum.push(`Redirect 301 ${fromUrl} ${toUrl}`)
}
return accum
}, [])
return [new File({ contents: Buffer.from(rules.join('\n') + '\n'), out: { path: '.htaccess' } })]
}
// NOTE: a trailing slash on the pathname will be ignored
// see https://docs.netlify.com/routing/redirects/redirect-options/#trailing-slash
// however, we keep it when generating the rules for clarity
function createNetlifyRedirects (files, urlPath, addDirectoryRedirects = false, useForceFlag = true) {
const rules = files.reduce((accum, file) => {
if (isRedirectLoop(file)) return accum
delete file.out
const fromUrl = urlPath + file.pub.url
const toUrl = urlPath + file.rel.pub.url
const forceFlag = useForceFlag ? '!' : ''
if (file.pub.splat) {
accum.push(`${fromUrl}/* ${ensureTrailingSlash(toUrl)}:splat 302${forceFlag}`)
} else {
accum.push(`${fromUrl} ${toUrl} 301${forceFlag}`)
if (addDirectoryRedirects && fromUrl.endsWith('/index.html')) {
accum.push(`${fromUrl.substr(0, fromUrl.length - 10)} ${toUrl} 301${forceFlag}`)
}
}
return accum
}, [])
return [new File({ contents: Buffer.from(rules.join('\n') + '\n'), out: { path: '_redirects' } })]
}
function createNginxRewriteConf (files, urlPath) {
const rules = files.map((file) => {
if (isRedirectLoop(file)) return ''
delete file.out
let fromUrl = file.pub.url
fromUrl = ~fromUrl.indexOf('%20') ? `'${urlPath}${fromUrl.replace(ENCODED_SPACE_RX, ' ')}'` : urlPath + fromUrl
let toUrl = file.rel.pub.url
toUrl = ~toUrl.indexOf('%20') ? `'${urlPath}${toUrl.replace(ENCODED_SPACE_RX, ' ')}'` : urlPath + toUrl
if (file.pub.splat) {
const toUrlWithTrailingSlash = ensureTrailingSlash(toUrl)
return `location ^~ ${fromUrl}/ { rewrite ^${regexpEscape(fromUrl)}/(.*)$ ${toUrlWithTrailingSlash}$1 redirect; }`
} else {
return `location = ${fromUrl} { return 301 ${toUrl}; }`
}
})
return [new File({ contents: Buffer.from(rules.join('\n').trim() + '\n'), out: { path: '.etc/nginx/rewrite.conf' } })]
}
function populateStaticRedirectFiles(files, siteUrl) {
for (const file of files) {
if (isRedirectLoop(file)) continue
const content = createStaticRedirectContents(file, siteUrl) + '\n';
file.contents = Buffer.from(content);
}
return []
}
Edited by Jake Cahill