diff --git a/news/changelog-1.10.md b/news/changelog-1.10.md
index d43a69482c4..da602c7a252 100644
--- a/news/changelog-1.10.md
+++ b/news/changelog-1.10.md
@@ -23,6 +23,6 @@ All changes included in 1.10:
## Other fixes and improvements
+- ([#14306](https://github.com/quarto-dev/quarto-cli/issues/14306)): Fix website Open Graph metadata for posts to include the missing `og:type` and `og:url` tags.
- ([#6651](https://github.com/quarto-dev/quarto-cli/issues/6651)): Fix dart-sass compilation failing in enterprise environments where `.bat` files are blocked by group policy.
- ([#14255](https://github.com/quarto-dev/quarto-cli/issues/14255)): Fix shortcodes inside inline and display math expressions not being resolved.
-
diff --git a/src/project/types/website/website-meta.ts b/src/project/types/website/website-meta.ts
index b70ac230d68..db6276f689c 100644
--- a/src/project/types/website/website-meta.ts
+++ b/src/project/types/website/website-meta.ts
@@ -8,6 +8,10 @@ import { Document, Element } from "../../../core/deno-dom.ts";
import { dirname, join, relative } from "../../../deno_ral/path.ts";
import {
kAbstract,
+ kAuthor,
+ kAuthors,
+ kDate,
+ kDateModified,
kDescription,
kNumberSections,
kSubtitle,
@@ -49,9 +53,12 @@ import { imageSize } from "../../../core/image.ts";
import { writeMetaTag } from "../../../format/html/format-html-shared.ts";
import { joinUrl } from "../../../core/url.ts";
import { truncateText } from "../../../core/text.ts";
+import { synthesizeCitationUrl } from "../../../quarto-core/attribution/document.ts";
import { websiteImage } from "./website-config.ts";
const kCard = "card";
+const kType = "type";
+const kUrl = "url";
interface SocialMetadataProvider {
key: string;
@@ -118,6 +125,17 @@ export function metadataHtmlPostProcessor(
return value;
},
+ resolveDefaults: (finalMetadata: Metadata) => {
+ if (finalMetadata[kType] === undefined) {
+ finalMetadata[kType] = openGraphType(format);
+ }
+ if (finalMetadata[kUrl] === undefined) {
+ const url = openGraphUrl(source, project, format);
+ if (url) {
+ finalMetadata[kUrl] = url;
+ }
+ }
+ },
};
// The twitter card provider
@@ -261,6 +279,8 @@ function opengraphMetadata(
kImageWidth,
kLocale,
kSiteName,
+ kType,
+ kUrl,
].forEach((key) => {
if (openGraph[key] !== undefined) {
metadata[key] = openGraph[key];
@@ -345,6 +365,28 @@ function resolveImageMetadata(
}
}
+function openGraphType(format: Format) {
+ return format.metadata[kDate] !== undefined ||
+ format.metadata[kDateModified] !== undefined ||
+ format.metadata[kAuthor] !== undefined ||
+ format.metadata[kAuthors] !== undefined
+ ? "article"
+ : "website";
+}
+
+function openGraphUrl(
+ source: string,
+ project: ProjectContext,
+ format: Format,
+) {
+ return synthesizeCitationUrl(
+ source,
+ format.metadata,
+ format.pandoc["output-file"],
+ relative(dirname(source), project.dir),
+ );
+}
+
function mergedSiteAndDocumentData(
key: string,
format: Format,
diff --git a/src/resources/types/schema-types.ts b/src/resources/types/schema-types.ts
index e2933f641eb..95a65ed95f1 100644
--- a/src/resources/types/schema-types.ts
+++ b/src/resources/types/schema-types.ts
@@ -331,6 +331,8 @@ export type OpenGraphConfig = {
provided in the `open-graph` metadata, Quarto will use the website or
book `title` by default. */;
locale?: string; /* Locale of open graph metadata */
+ type?: string; /* Content to use for the `og:type` tag */
+ url?: string; /* Content to use for the `og:url` tag */
} & SocialMetadata;
export type PageFooter = {
diff --git a/src/resources/types/zod/schema-types.ts b/src/resources/types/zod/schema-types.ts
index 1471ea898f4..90a3b8a0464 100644
--- a/src/resources/types/zod/schema-types.ts
+++ b/src/resources/types/zod/schema-types.ts
@@ -269,6 +269,8 @@ export const ZodOpenGraphConfig = z.object({
"image-height": z.number(),
locale: z.string(),
"site-name": z.string(),
+ type: z.string(),
+ url: z.string(),
}).strict().partial();
export const ZodPageFooter = z.object({
diff --git a/tests/docs/smoke-all/2026/04/02/og-type-url/_quarto.yml b/tests/docs/smoke-all/2026/04/02/og-type-url/_quarto.yml
new file mode 100644
index 00000000000..0f1c3e2247c
--- /dev/null
+++ b/tests/docs/smoke-all/2026/04/02/og-type-url/_quarto.yml
@@ -0,0 +1,9 @@
+project:
+ type: website
+
+website:
+ title: "Open Graph Type URL"
+ site-url: https://example.com
+ open-graph: true
+
+format: html
diff --git a/tests/docs/smoke-all/2026/04/02/og-type-url/index.qmd b/tests/docs/smoke-all/2026/04/02/og-type-url/index.qmd
new file mode 100644
index 00000000000..17325bcf838
--- /dev/null
+++ b/tests/docs/smoke-all/2026/04/02/og-type-url/index.qmd
@@ -0,0 +1,5 @@
+---
+title: "Home"
+---
+
+Home page.
diff --git a/tests/docs/smoke-all/2026/04/02/og-type-url/posts/a-test-post/index.qmd b/tests/docs/smoke-all/2026/04/02/og-type-url/posts/a-test-post/index.qmd
new file mode 100644
index 00000000000..7c25bdf562c
--- /dev/null
+++ b/tests/docs/smoke-all/2026/04/02/og-type-url/posts/a-test-post/index.qmd
@@ -0,0 +1,17 @@
+---
+title: "A Test Post"
+description: "A test post for Open Graph metadata."
+date: "2026-04-02"
+author: "Quarto Tester"
+_quarto:
+ render-project: true
+ tests:
+ html:
+ ensureFileRegexMatches:
+ -
+ - ''
+ - ''
+ - []
+---
+
+This is a test post.
diff --git a/tests/docs/smoke-all/2026/04/02/og-type-url/posts/override-og/index.qmd b/tests/docs/smoke-all/2026/04/02/og-type-url/posts/override-og/index.qmd
new file mode 100644
index 00000000000..d3875775c81
--- /dev/null
+++ b/tests/docs/smoke-all/2026/04/02/og-type-url/posts/override-og/index.qmd
@@ -0,0 +1,21 @@
+---
+title: "Override Open Graph"
+description: "A test post for Open Graph metadata overrides."
+date: "2026-04-02"
+author: "Quarto Tester"
+website:
+ open-graph:
+ type: website
+ url: https://www.example.com/
+_quarto:
+ render-project: true
+ tests:
+ html:
+ ensureFileRegexMatches:
+ -
+ - ''
+ - ''
+ - []
+---
+
+This is a test post with overridden Open Graph metadata.