Skip to content

Commit 4ecb7b6

Browse files
chore(site): seo improvements (#4157)
# Description Please include a summary of the changes and the related issue. Please also include relevant motivation and context. ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes
1 parent 7072569 commit 4ecb7b6

File tree

11 files changed

+140
-17
lines changed

11 files changed

+140
-17
lines changed

examples/hono-vercel/README.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/hono/README.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/astro.config.mjs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,32 @@ export default defineConfig({
1414
site: 'https://rivet.dev',
1515
output: 'static',
1616
trailingSlash: 'ignore',
17+
// SEO Redirects - Astro generates HTML redirect files for static builds
18+
// These work in dev server and all deployment platforms (Vercel, Netlify, Cloudflare, etc.)
19+
redirects: {
20+
// Documentation restructure
21+
'/docs/setup': '/docs/actors/quickstart/',
22+
'/docs/actors/queues': '/docs/actors/queue/',
23+
'/docs/actors/websockets': '/docs/actors/websocket-handler/',
24+
'/docs/actors/http': '/docs/actors/http-api/',
25+
'/docs/actors/run': '/docs/actors/lifecycle/',
26+
'/docs/actors/scheduling': '/docs/actors/schedule/',
27+
// Platform docs moved to clients/connect
28+
'/docs/platforms/react': '/docs/clients/react/',
29+
'/docs/platforms/next-js': '/docs/clients/javascript/',
30+
'/docs/platforms/cloudflare-workers': '/docs/connect/cloudflare-workers/',
31+
// Registry configuration moved
32+
'/docs/connect/registry-configuration': '/docs/general/registry-configuration/',
33+
// Cloud docs removed - redirect to relevant sections
34+
'/docs/cloud': '/docs/self-hosting/',
35+
'/docs/cloud/api/actors/create': '/docs/actors/',
36+
'/docs/cloud/api/routes/update': '/docs/actors/',
37+
'/docs/cloud/self-hosting/single-container': '/docs/self-hosting/docker-container/',
38+
// Next.js client redirect (linked from homepage)
39+
'/docs/clients/next-js': '/docs/clients/javascript/',
40+
// Self-hosting redirect
41+
'/docs/general/self-hosting': '/docs/self-hosting/',
42+
},
1743
prefetch: {
1844
prefetchAll: true,
1945
defaultStrategy: 'hover',

website/public/robots.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
User-agent: *
33
Allow: /
44

5-
# Sitemap
6-
Sitemap: https://rivet.dev/sitemap.xml
7-
8-
# Disallow API and internal routes
5+
# Disallow API, internal routes, and TypeDoc (generic meta descriptions)
96
Disallow: /api/
107
Disallow: /internal/
8+
Disallow: /typedoc/
9+
10+
# Sitemap
11+
Sitemap: https://rivet.dev/sitemap-index.xml

website/src/content/docs/general/architecture.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,8 @@ TODO: copy the rest of this from low-level webosckets document and rephrase
368368
### networking
369369

370370
- actors may live in different regions than inbound requests
371-
- rive tuses the Epoxy (link again) system to handle global routing to route traffic to the correct region with high performance
372-
- this is completely transparent to you. your app sends traffic to https://api.rivet.dev/gatewa/* and it automatically routes to the correct actor in the appropirate region
371+
- Rivet uses the Epoxy (link again) system to handle global routing to route traffic to the correct region with high performance
372+
- this is completely transparent to you. your app sends traffic to https://api.rivet.dev/gateway/* and it automatically routes to the correct actor in the appropriate region
373373

374374
### globally unique actor keys
375375

website/src/layouts/BaseLayout.astro

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@ interface Props {
1818
ogType?: string;
1919
publishedTime?: string;
2020
keywords?: string[];
21+
articleAuthor?: string;
22+
articleSection?: string;
23+
robots?: string;
2124
}
2225
26+
const DEFAULT_KEYWORDS = ["rivet", "actors", "serverless", "backend", "real-time", "stateful", "websockets"];
27+
2328
const {
2429
title,
2530
description = "Rivet Actors are long-lived stateful processes with built-in state, storage, workflows, scheduling, WebSockets, and more. Open source and self-hostable.",
@@ -28,29 +33,51 @@ const {
2833
ogType = "website",
2934
publishedTime,
3035
keywords,
36+
articleAuthor,
37+
articleSection,
38+
robots,
3139
} = Astro.props;
3240
41+
// Normalize keywords to lowercase for consistency
42+
const effectiveKeywords = Array.isArray(keywords)
43+
? keywords.map(k => k.toLowerCase())
44+
: DEFAULT_KEYWORDS;
45+
46+
// Only add " - Rivet" suffix if not already present (handles Rivet, Rivet Actors, Rivet Templates)
47+
const formattedTitle = !title
48+
? 'Rivet'
49+
: / - Rivet( Actors| Templates)?$/.test(title)
50+
? title
51+
: `${title} - Rivet`;
52+
3353
const siteUrl = import.meta.env.PUBLIC_SITE_URL || 'https://rivet.dev';
3454
3555
// Auto-generate canonical URL from current page path if not explicitly provided
3656
const currentPath = Astro.url.pathname;
3757
const effectiveCanonicalUrl = canonicalUrl || `https://rivet.dev${currentPath}`;
58+
59+
// RSS feed is only relevant for blog and changelog pages
60+
const showRssFeed = currentPath.startsWith('/blog') || currentPath.startsWith('/changelog');
3861
---
3962

4063
<!DOCTYPE html>
4164
<html lang="en" class="dark">
4265
<head>
4366
<meta charset="utf-8" />
4467
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
45-
<title>{title}</title>
68+
<title>{formattedTitle}</title>
4669
<meta name="description" content={description} />
70+
<meta http-equiv="content-language" content="en" />
71+
{robots && <meta name="robots" content={robots} />}
72+
<meta name="keywords" content={effectiveKeywords.join(', ')} />
4773

4874
<link rel="canonical" href={effectiveCanonicalUrl} />
75+
{showRssFeed && <link rel="alternate" type="application/rss+xml" title="Rivet Blog" href="/rss/feed.xml" />}
4976

5077
<!-- Twitter -->
5178
<meta name="twitter:site" content="@rivet_dev" />
5279
<meta name="twitter:card" content="summary_large_image" />
53-
<meta name="twitter:title" content={title} />
80+
<meta name="twitter:title" content={formattedTitle} />
5481
<meta name="twitter:description" content={description} />
5582
<meta name="twitter:image" content={ogImage} />
5683

@@ -59,14 +86,16 @@ const effectiveCanonicalUrl = canonicalUrl || `https://rivet.dev${currentPath}`;
5986
<meta property="og:locale" content="en_US" />
6087
<meta property="og:url" content={effectiveCanonicalUrl} />
6188
<meta property="og:site_name" content="Rivet" />
62-
<meta property="og:title" content={title} />
89+
<meta property="og:title" content={formattedTitle} />
6390
<meta property="og:description" content={description} />
6491
<meta property="og:image" content={ogImage} />
92+
<meta property="og:image:alt" content={title ? `${title} - Rivet Actors` : 'Rivet Actors'} />
6593
<meta property="og:image:width" content="1200" />
6694
<meta property="og:image:height" content="630" />
6795
{publishedTime && <meta property="article:published_time" content={publishedTime} />}
68-
69-
{keywords && <meta name="keywords" content={keywords.join(', ')} />}
96+
{articleAuthor && <meta property="article:author" content={articleAuthor} />}
97+
{articleSection && <meta property="article:section" content={articleSection} />}
98+
{Array.isArray(keywords) && keywords.map((keyword) => <meta property="article:tag" content={keyword.toLowerCase()} />)}
7099

71100
<!-- JSON-LD Structured Data -->
72101
<script type="application/ld+json" set:html={JSON.stringify({

website/src/layouts/BlogLayout.astro

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@ interface Props {
1111
modifiedTime?: string;
1212
keywords?: string[];
1313
authorName?: string;
14+
authorSocials?: { twitter?: string; github?: string; bluesky?: string };
15+
category?: string;
1416
}
1517
16-
const { title, description, canonicalUrl, ogImage, publishedTime, modifiedTime, keywords, authorName } = Astro.props;
18+
const { title, description, canonicalUrl, ogImage, publishedTime, modifiedTime, keywords, authorName, authorSocials, category } = Astro.props;
19+
20+
// Build sameAs array from author socials
21+
const authorSameAs = authorSocials ? Object.values(authorSocials).filter(Boolean) : [];
1722
1823
const articleSchema = {
1924
"@context": "https://schema.org",
@@ -25,7 +30,8 @@ const articleSchema = {
2530
"dateModified": modifiedTime || publishedTime,
2631
"author": {
2732
"@type": "Person",
28-
"name": authorName || "Rivet Team"
33+
"name": authorName || "Rivet Team",
34+
...(authorSameAs.length > 0 && { "sameAs": authorSameAs })
2935
},
3036
"publisher": {
3137
"@type": "Organization",
@@ -52,6 +58,8 @@ const articleSchema = {
5258
ogType="article"
5359
publishedTime={publishedTime}
5460
keywords={keywords}
61+
articleAuthor={authorName}
62+
articleSection={category}
5563
>
5664
<script type="application/ld+json" set:html={JSON.stringify(articleSchema)} />
5765
<Header active="blog" variant="floating" client:load />

website/src/pages/blog/[...slug].astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ const breadcrumbSchema = {
105105
publishedTime={entry.data.published.toISOString()}
106106
keywords={entry.data.keywords}
107107
authorName={author.name}
108+
authorSocials={author.socials}
109+
category={category.name}
108110
>
109111
<script type="application/ld+json" set:html={JSON.stringify(breadcrumbSchema)} />
110112
<div

website/src/pages/changelog/[...slug].astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ const otherArticles = filteredOtherPosts.map((post) => {
9191
publishedTime={entry.data.published.toISOString()}
9292
keywords={entry.data.keywords}
9393
authorName={author.name}
94+
authorSocials={author.socials}
95+
category={category.name}
9496
>
9597
<div class="mx-auto mt-20 w-full max-w-6xl px-4 md:mt-32" style="--header-height: 5rem;">
9698
<ul class="text-muted-foreground my-4 flex flex-wrap items-center gap-2 text-xs">

website/src/pages/cloud.astro

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,67 @@
11
---
22
import MarketingLayout from '@/layouts/MarketingLayout.astro';
33
import PricingPageClient from '@/components/marketing/pricing/PricingPageClient';
4+
5+
// Product schema with pricing tiers
6+
const productSchema = {
7+
"@context": "https://schema.org",
8+
"@type": "Product",
9+
"name": "Rivet Cloud",
10+
"description": "Managed cloud solution for stateful backends. Global infrastructure, persistent state, and seamless orchestration for your actors.",
11+
"brand": {
12+
"@type": "Brand",
13+
"name": "Rivet"
14+
},
15+
"url": "https://rivet.dev/cloud/",
16+
"image": "https://rivet.dev/promo/og.png",
17+
"category": "Cloud Computing Platform",
18+
"offers": [
19+
{
20+
"@type": "Offer",
21+
"name": "Free",
22+
"description": "For prototyping and small projects. 100,000 Awake Actor Hours, 5GB storage, community support.",
23+
"price": "0",
24+
"priceCurrency": "USD",
25+
"priceValidUntil": "2027-12-31",
26+
"availability": "https://schema.org/InStock",
27+
"url": "https://dashboard.rivet.dev"
28+
},
29+
{
30+
"@type": "Offer",
31+
"name": "Hobby",
32+
"description": "For scaling applications. 400,000 Awake Actor Hours included, 25 Billion reads/month, email support.",
33+
"price": "20",
34+
"priceCurrency": "USD",
35+
"priceValidUntil": "2027-12-31",
36+
"availability": "https://schema.org/InStock",
37+
"url": "https://dashboard.rivet.dev"
38+
},
39+
{
40+
"@type": "Offer",
41+
"name": "Team",
42+
"description": "For growing teams and businesses. 400,000 Awake Actor Hours included, MFA, Slack support.",
43+
"price": "200",
44+
"priceCurrency": "USD",
45+
"priceValidUntil": "2027-12-31",
46+
"availability": "https://schema.org/InStock",
47+
"url": "https://dashboard.rivet.dev"
48+
},
49+
{
50+
"@type": "Offer",
51+
"name": "Enterprise",
52+
"description": "For high-volume, mission-critical workloads. Priority support, SLA, OIDC SSO, audit logs, custom roles.",
53+
"availability": "https://schema.org/InStock",
54+
"url": "https://rivet.dev/sales/"
55+
}
56+
]
57+
};
458
---
559

660
<MarketingLayout
761
title="Cloud - Rivet"
862
description="Rivet Cloud—managed cloud solution for stateful backends. Transparent pricing, global infrastructure, and seamless orchestration."
963
canonicalUrl="https://rivet.dev/cloud/"
1064
>
65+
<script type="application/ld+json" set:html={JSON.stringify(productSchema)} />
1166
<PricingPageClient client:load />
1267
</MarketingLayout>

0 commit comments

Comments
 (0)