1338 words
7 minutes
Creating a Webring

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.

usc-cisco
/
ring
Waiting for api.github.com...
00K
0K
0K
Waiting...

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 .com domains 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:

Link Embed Component

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: 1
  • I had to set up SSH_USERNAME and SSH_PASSWORD secrets in the GitHub repository settings before triggering the build.

  • overwrite: true is used to update any old files with new data.

  • strip_components: 1 is used to remove the dist directory in the path when copying because the appleboy/scp-action would always copy the entire dist/ 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!

Creating a Webring
https://ian.dcism.org/posts/creating-a-webring/
Author
typegeneric
Published at
2025-04-14