Sep 16 2022
Aug 31 2022
Back in 2019, we migrated our developer documentation at developer.stackpath.com
to a new platform. Our documentation site worked well enough, and we had just added a new Edge Computing API service to it. But with object storage and other products on the horizon, we were concerned about how to fit all these cool new products into our developer site. We also wanted to give users the ability to make live API calls from our API reference while making the deployment process easier for us. This way, we could get our users quality API documentation much faster.
After much discussion, searching, and testing, we ended up rolling out a new documentation site on our shiny new stackpath.dev domain. A bit of content migration and swagger importing got us an API reference that’s easier for us to manage and more handy for our users. Unfortunately, the paths of the old and new URLs didn’t match, leaving us with one last problem: how should we redirect requests from the old developer site to the new one?
Both our old and new documentation sites were on StackPath’s edge network, so we had plenty of options:
developer.stackpath.com
to stackpath.dev
as a DNS CNAME record. That’s easy to do, and it does the bare minimum by redirecting requests from the old domain to the new one. On the other hand, we can’t redirect individual article requests to their place on the new site.stackpath.dev
. That’s a pretty standard IT practice and would do the job perfectly. Unfortunately, I’m just a developer and don’t have access to the web server’s configuration. Also, we don’t want to have to re-deploy a configuration every time we tweak redirection rules. /
path can handle every URL redirect we need. Since it’s all code we can customize our redirects, notify us on redirect, or not redirect requests as we see fit. We also love dogfooding our products. Serverless scripting works great, so why not use it?Option 4 was definitely the right choice for us. We wrote a simple redirect script that defines source and destination paths and returns HTTP 301 responses on requests to developer.stackpath.com
along with a Location
header pointing the browser to the equivalent article at stackpath.dev
. Returning a 301 response performs browser redirection the way we like and preserves our developer documentation’s SEO.
Serverless scripting gives you an enormous level of flexibility—not just for site content, but for common devops tasks like these.
Our redirection is live and mirrored on our serverless examples repository on GitHub.
// The site to redirect requests to.
const redirectHost = 'https://stackpath.dev';
// A map of paths at the original site and new paths to redirect to.
const redirects = new Map([
// The home page
['/', ''],
['/en/', ''],
// The welcome section
['/docs/en/getting-started/', 'docs/getting-started'],
// CDN guides
['/docs/en/cdn/static-site-s3/', 'docs/static-site-with-s3'],
['/docs/en/cdn/getting-stack-metrics/', 'docs/getting-stack-metrics'],
// Serverless scripting guides
['/docs/en/EdgeEngine/introduction/', 'docs/introduction'],
['/docs/en/EdgeEngine/edgeengine-quickstart/', 'docs/getting-started-1'],
['/docs/en/EdgeEngine/available-apis/', 'docs/available-apis'],
['/docs/en/EdgeEngine/debug/', 'docs/debugging'],
['/docs/en/EdgeEngine/cli/', 'docs/edgeengine-cli'],
['/docs/en/EdgeEngine/block-countries/', 'docs/block-countries'],
['/docs/en/EdgeEngine/cookies/', 'docs/cookies'],
['/docs/en/EdgeEngine/crypto/', 'docs/crypto'],
['/docs/en/EdgeEngine/in-memory-caching/', 'docs/in-memory-caching'],
['/docs/en/EdgeEngine/modify-headers/', 'docs/modify-headers'],
['/docs/en/EdgeEngine/modify-response-body/', 'docs/modify-response-body'],
['/docs/en/EdgeEngine/request-header-variables/', 'docs/request-header-variables'],
['/docs/en/EdgeEngine/static-response/', 'docs/static-response'],
// Edge computing guides
['/docs/en/edgecomputing/creating-a-workload/', 'docs/create-a-container-workload'],
// API references
['/en/api/identity/', 'reference/accounts'],
['/en/api/dns/', 'reference/scanning'],
['/en/api/cdn/', 'reference/infrastructure'],
['/en/api/workload/', 'reference/workloads'],
['/en/api/waf/', 'reference/infrastructure-2'],
['/en/api/monitoring/', 'reference/http-monitoring'],
['/en/api/stacks/', 'reference/stacks'],
]);
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
/**
* Redirect requests from developer.stackpath.com to stackpath.dev.
*
* @param {Request} request
*/
async function handleRequest(request) {
try {
// Make sure the request path ends with a / so map gets will work for
// requests without a trailing slash.
let path = new URL(request.url).pathname;
if (!path.endsWith('/')) {
path += '/';
}
const redirectTo = redirects.get(path);
// If there wasn't a redirect defined then return the fetched original
// request.
//
// Consider logging when this happens.
if (redirectTo === undefined) {
const response = await fetch(request);
return response;
}
// Otherwise, redirect the user to the new URL.
return new Response(null, {
status: 301,
statusText: "Moved Permanently",
headers: {
Location: `${redirectHost}/${redirectTo}`
}
});
} catch (e) {
return new Response(e.stack || e, { status: 500 });
}
}
Feel free to use this for your sites or open an issue or pull request if you have feedback or improvements!