Creating a Webring
Webrings are a fascinating part of the internet that allows people to connect and create a network of websites that follow a similar theme or have a certain affinity for one another. And today, I created one using Astro as a sort of experiment, while also giving back to my community.
For more context, I’ve been exploring this rabbit hole for a few weeks now after stumbling across a “friends” section in someone’s personal website. It was a page full of links to other personal websites, each with their own flair & personality. Jumping through these websites felt extremely nostalgic and made me realize that I could make one of my own.
So after an afternoon, I managed to create ring.dcism.org, the official webring for DCISM students & alumni.
What exactly is a webring?
Here’s a general idea of a webring, based on Wikipedia’s definition.
A webring (or web ring) is a collection of websites linked together in a circular structure, usually organized around a specific theme, and often educational or social. They were popular in the 1990s and early 2000s, particularly among amateur websites.
So essentially, it’s a thematic directory or index website containing links to its members in order to drive more traffic to their websites.
Why don’t people do this more often?
Outside of the relatively high knowledge barrier (for normies), maintaining a webring or being a part of one isn’t that easy.
For instance, domains and servers aren’t free. You don’t only pay with money, but also with the maintenance time needed to ensure that; your SSL certificates are valid, your website stays up, your website is secure, etc.
How can I create or join a webring?
There are a lot of JavaScript, API, Markdown (Jamstack) tools that help alleviate these issues. For instance, you can deploy your site to GitHub Pages, Vercel, or Netlify to automatically obtain valid SSL certificates and a secure server for static files - which is enough for most personal sites!
The only thing you truly can’t obtain for free is a domain, but a good TLD like .com usually goes for $11 per year.
You can purchase
.comdomains and other similar well-known domains using spaceship.com. They are the most affordable site to do so - while also having services like email, SSL, bulk searching, and domain transfers.
Once you own a domain, you can search up how to connect the domain to your hosting platform of choice and then you can start building & showcasing your personal sites to others!
Good Technology Stacks
If you have experience, you can simply write your static website using vanilla HTML, CSS, and JavaScript. But it isn’t 2012 anymore. We can rely on tools like Next.js and Astro to help us bundle our assets into a static dist/ folder which we can deploy to our hosting platform.
The nice thing about Astro is that it’s specialized for static content sites like blogs, portfolios, and personal websites. For instance, this website was built with Astro and Svelte. It loads all my markdown files and renders them into HTML using remark, then Astro generates routes for them.
And if I need any interactive component, I can use Svelte to create a .svelte component and import it using Astro to dynamically hydrate it on the client. This is achieved using the Astro Islands architecture - allowing interop of dynamic and static content. It’s one of the many amazing features Astro provides.
If Svelte isn’t your favorite framework, Astro also supports React, Vue, SolidJS, AlpineJS, and Preact.
What if I suck at building websites?
Astro offers hundreds of open-source themes which are ready-to-edit websites which can be personalized to your liking. Simply clone a repository and start hacking away!
Building the DCISM Webring
The philosophy behind ring.dcism.org is quite simple: create a website that links personal sites together and allows dynamic updates through GitHub pull requests.
Validating Entries
In order to do this, I had to setup a way to create a standardized format which validates new entries. That’s why I used TypeScript to define a webring[] array which is typed to the format of a webring.
export type WebringEntry = {
name: string; // Full name
year: number; // Graduation (or expected) year
url: string; // Full website URL
};
export const webring: WebringEntry[] = [
{
name: "typegeneric",
year: 2026,
url: "https://ian.dcism.org",
},
];With this implementation, TypeScript can parse the data during the build step and gracefully cancel the build if an entry doesn’t comply with the format.
Generating a Dynamic Webring Linker
Now, since the webring[] array is static, I can take advantage of Astro’s static site generation functionality to prepopulate the props of API routes, based on the webring name property.
Then, I simply link to the previous & next person in the webring[] using the modulo operator in order to make the linking akin to an actual “circular” webring - if people actually include the embed on their websites.
import { SITE_CONFIG } from "@/config/site.config";
import { webring } from "@/data/webring";
import type { APIContext } from "astro";
export const prerender = true;
export function getStaticPaths() {
return webring.map((entry, i) => {
const prevIndex = (i - 1 + webring.length) % webring.length;
const nextIndex = (i + 1) % webring.length;
return {
params: { name: entry.name },
props: {
prevEntry: webring[prevIndex],
nextEntry: webring[nextIndex],
},
};
});
}
export function GET({ props }: APIContext) {
const { prevEntry, nextEntry } = props;
const webringNavHtml = `
<div class="webring-navigation" style="display: flex; align-items: center; justify-items: center; gap: 0.5rem;">
<a style="color: black;" href="${prevEntry.url}"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 6l-6 6l6 6" /></svg></a>
<a href="${SITE_CONFIG.baseUrl}"><img style="filter: invert(1); width: 16px; height: 16px; rotate: 25deg;" src="${SITE_CONFIG.baseUrl}/logo.svg"></img></a>
<a style="color: black;" href="${nextEntry.url}"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 6l6 6l-6 6" /></svg></a>
</div>
`;
return new Response(webringNavHtml, {
headers: { "Content-Type": "text/html" },
});
}Here’s what it looks like when you make a GET request on the API route based on a member’s name:

The downside is that I need to rebuild the entire site in order for the webring to stay updated. However, this isn’t an issue with Astro’s ~900ms build times on GitHub Actions.
DCISM Server CI/CD
Now, since the DCISM server blocks all outgoing connections to github.com or google.com (like why?), I had to to build the website using GitHub Actions and scp it to the server using my ssh credentials. Here’s the .github/workflows/build.yaml file:
name: Deploy Frontend Build
on:
workflow_dispatch:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install dependencies
run: npm i
- name: Build the site
run: npm run build
- name: Upload via SCP
uses: appleboy/scp-action@v0.1.7
with:
host: dcism.org
username: ${{ secrets.SSH_USERNAME }}
password: ${{ secrets.SSH_PASSWORD }}
port: 22077
source: "dist/" # static project files output directory
target: "~/ring.dcism.org/" # change this to your domain
overwrite: true
strip_components: 1I had to set up
SSH_USERNAMEandSSH_PASSWORDsecrets in the GitHub repository settings before triggering the build.overwrite: trueis used to update any old files with new data.strip_components: 1is used to remove thedistdirectory in the path when copying because theappleboy/scp-actionwould always copy the entiredist/directory into~/ring.dcism.org, which isn’t ideal for my Apache setup.
Now, in order to serve the files directly to the domain in the DCISM server, I had to setup this .htaccess file:
# Make the root domain request index.html to load the site
DirectoryIndex index.html
RewriteEngine on
# Upgrade all HTTP connections to HTTPS while maintaining URL query params & path
RewriteCond %{SERVER_PORT} 80
RewriteRule ^.*$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]Once that’s all set, I’m able to build & deploy any Astro website to the DCISM server.
Final Thoughts
Creating this project was a fun experience that made me appreciate the beauty and simplicity of Astro. It also made me realize that my old projects on the DCISM server are no longer updating due to the fact that outgoing connections are blocked.
But until then, this project is alive and well and is accepting Pull Requests for new members! Just read the README.md file first for contribution guidelines.
Happy coding!


