Using page aliases combined with the indexify style 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