🇬🇧 English
You put a site on GitHub Pages. Nobody comes. That is normal, and it is not a mystery.
Google has to find the site, decide to read it, and decide to keep it. Search Console is where you watch all three.
This page sets it up for a github.io site, which has one restriction the guides skip.
🇹🇭 ไทย
คุณเอาเว็บขึ้น GitHub Pages แล้วไม่มีใครเข้ามาเลย นั่นเป็นเรื่องปกติ และไม่ใช่เรื่องลึกลับ
Google ต้องหาเว็บให้เจอ ตัดสินใจว่าจะอ่าน และตัดสินใจว่าจะเก็บไว้ Search Console คือที่ที่คุณเฝ้าดูทั้งสามอย่างนั้น
หน้านี้ตั้งค่าให้เว็บแบบ github.io ซึ่งมีข้อจำกัดหนึ่งอย่างที่คู่มือทั่วไปมักข้ามไป
🇨🇳 中文
你把网站放上了 GitHub Pages,然后没人来。这很正常,也不神秘。
Google 得先找到这个站、决定去读它、再决定留着它。Search Console 就是你看这三件事的地方。
这一页针对 github.io 网站来设置,它有一个限制是别的指南会跳过的。
| GitHub Pages gives youGitHub Pages ให้อะไรGitHub Pages 给你什么 | You have to addคุณต้องเพิ่มเอง你得自己加 | |
|---|---|---|
| Hostingโฮสติ้ง托管 | Pages: free, on httpsPages: ฟรี และเป็น httpsPages:免费,走 https | you: nothingคุณ: ไม่ต้องทำอะไร你:什么都不用 |
| robots.txtrobots.txtrobots.txt | Pages: nothing, it is a 404Pages: ไม่มี ขึ้น 404Pages:没有,是 404 | you: write oneคุณ: เขียนเอง你:自己写一个 |
| sitemap.xmlsitemap.xmlsitemap.xml | Pages: nothingPages: ไม่มีPages:没有 | you: generate itคุณ: สร้างขึ้นมา你:自己生成 |
| Told Google it existsบอก Google ว่ามีเว็บนี้告诉过 Google 它存在 | Pages: noPages: ไม่Pages:没有 | you: Search Consoleคุณ: ใช้ Search Console你:用 Search Console |
github.io address has none pointing at it. There is nothing to follow. Everything on this page is about giving it a way in. Expect days, not minutes, even after you do it right.
Google หาหน้าเว็บด้วยการเดินตามลิงก์ ที่อยู่ github.io ที่เพิ่งสร้างใหม่ยังไม่มีลิงก์ไหนชี้มาเลย จึงไม่มีอะไรให้เดินตาม ทุกอย่างในหน้านี้คือการเปิดทางให้มันเข้ามา ให้คาดหวังเป็นวัน ไม่ใช่เป็นนาที ต่อให้คุณทำถูกหมดแล้วก็ตาม
Google 靠跟着链接找页面。一个刚建好的 github.io 地址没有任何链接指向它,所以没有东西可跟。这一页讲的都是给它开一条路。就算你全做对了,也要按天算,不是按分钟算。
🌱 Tutorial 1 — A site Google can read · บทที่ 1 — เว็บที่ Google อ่านได้ · 教程 1 —— 一个 Google 读得懂的网站
🇬🇧 English
First the site itself, then a look at your own pages the way a search engine sees them.
🇹🇭 ไทย
เริ่มจากตัวเว็บก่อน แล้วค่อยดูหน้าเว็บของคุณเอง ในแบบที่เสิร์ชเอนจินมองเห็น
🇨🇳 中文
先是网站本身,然后用搜索引擎的眼光看一遍你自己的页面。
1 The repository, and what Pages does not give you · รีโพ และสิ่งที่ Pages ไม่ได้ให้มา · 仓库,以及 Pages 没给你的东西
🇬🇧 English
A repository named yourname.github.io becomes a website at that address. That is the whole setup.
What it does not come with is anything that helps a search engine. Both of these are 404 on a fresh site.
🇹🇭 ไทย
รีโพที่ชื่อ yourname.github.io จะกลายเป็นเว็บไซต์ที่อยู่นั้น การตั้งค่ามีแค่นั้น
สิ่งที่มันไม่ได้แถมมาคืออะไรก็ตามที่ช่วยเสิร์ชเอนจิน ทั้งสองอย่างนี้ขึ้น 404 บนเว็บที่เพิ่งสร้าง
🇨🇳 中文
一个叫 yourname.github.io 的仓库,就会变成那个地址上的网站。设置就这么多。
它没附带任何帮搜索引擎的东西。在一个全新的站上,这两个都是 404。
💻 MAKE IT · at the terminal, once
# The name matters. It must be exactly yourname.github.io
gh repo create yourname.github.io --public --clone
cd yourname.github.io
echo '<!doctype html><html lang="en"><head><title>Hello</title>'\
'</head><body><h1>Hello</h1></body></html>' > index.html
git add index.html && git commit -m 'first page' && git push
# Settings -> Pages -> Source: main. Then wait a minute.
🖥️ WHAT A FRESH ONE LOOKS LIKE · measured on a real Pages site
https://octocat.github.io/ 200
https://octocat.github.io/robots.txt 404 <- nothing there
https://octocat.github.io/sitemap.xml 404 <- nothing there
X-Robots-Tag: not set <- good, nothing blocks you
X-Robots-Tag: noindex. Nothing on the page shows it. GitHub Pages does not set it, which the measurement above confirms. If you later move to another host, check again. Step 4 does it for you.
เฮดเดอร์เดียวคือ X-Robots-Tag: noindex ซ่อนทั้งเว็บจากการค้นหาได้ และไม่มีอะไรบนหน้าเว็บที่แสดงให้เห็น GitHub Pages ไม่ได้ตั้งค่านั้น ซึ่งการวัดข้างบนยืนยันแล้ว ถ้าวันหลังย้ายไปโฮสต์อื่น ให้ตรวจใหม่ ขั้นที่ 4 ตรวจให้คุณเอง
一个响应头 X-Robots-Tag: noindex 就能把整个站从搜索里藏起来,而页面上什么都看不出来。GitHub Pages 不设它,上面的实测确认了这点。以后换别的主机,要再查一次。第 4 步会替你查。
- Make the repository. Push one page. Open the address.
- Now open
/robots.txton your own site. A 404. - Open
/sitemap.xml. Also a 404. - Those two are steps 3 and 4.
- สร้างรีโพ พุชหนึ่งหน้า แล้วเปิดที่อยู่นั้น
- ทีนี้เปิด
/robots.txtบนเว็บของคุณ จะได้ 404 - เปิด
/sitemap.xmlก็ได้ 404 เหมือนกัน - สองอันนั้นคือขั้นที่ 3 กับ 4
- 建好仓库,推一个页面,打开那个地址。
- 现在打开你自己站上的
/robots.txt,404。 - 再打开
/sitemap.xml,也是 404。 - 这两个就是第 3 步和第 4 步。
2 Read your own pages first · อ่านหน้าเว็บของตัวเองก่อน · 先读一遍你自己的页面
🇬🇧 English
Before asking Google to look, look yourself. A search engine reads about five things from a page.
If you have no site yet, the first file makes four pages to practise on, two of them deliberately wrong.
🇹🇭 ไทย
ก่อนจะขอให้ Google มาดู ลองดูเองก่อน เสิร์ชเอนจินอ่านจากหน้าเว็บอยู่ราวห้าอย่างเท่านั้น
ถ้ายังไม่มีเว็บ ไฟล์แรกจะสร้างสี่หน้าให้ลองเล่น โดยตั้งใจทำให้ผิดไว้สองหน้า
🇨🇳 中文
在请 Google 来看之前,自己先看。搜索引擎从一个页面上读的东西大约就五样。
如果你还没有站,第一个文件会做四个页面给你练手,其中两个是故意写错的。
📄 NEW FILE · make_sample.py
"""Build a small site to test the scripts against, the shape a student's is.
Four pages, one of them deliberately missing its description and sharing a
title with another, so the checker in step 5 has something to find.
"""
import sys
from pathlib import Path
SITE = Path(__file__).parent / "site"
sys.stdout.reconfigure(encoding="utf-8")
PAGE = """<!doctype html>
<html lang="{lang}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{title}</title>
{description}
</head>
<body>
<h1>{heading}</h1>
<p>{body}</p>
</body>
</html>
"""
DESC = '<meta name="description" content="{0}">'
PAGES = {
"index.html": dict(
lang="en", title="Nari's notebook",
description=DESC.format("Notes on learning Python in Chiang Mai, "
"with small programs you can run."),
heading="Nari's notebook",
# The front page links to one of the three. The other is an orphan,
# which is what the last challenge goes looking for.
body='Everything I have worked out so far. '
'<a href="python.html">Learning Python</a>.'),
"python.html": dict(
lang="en", title="Learning Python",
description=DESC.format("How I started with Python, and the first "
"five things that confused me."),
heading="Learning Python", body="Start at the prompt, not in a file."),
"khao-soi.html": dict(
lang="en", title="Learning Python",
description="",
heading="Khao soi", body="The best bowl is on Nimmanhaemin."),
"drafts/notes.html": dict(
lang="en", title="Rough notes",
description=DESC.format("Half-finished notes, not meant for anyone."),
heading="Rough notes", body="Do not read this yet."),
}
def main():
for name, fields in PAGES.items():
target = SITE / name
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(PAGE.format(**fields), encoding="utf-8")
print(f" wrote {name}")
print(f"{len(PAGES)} pages in {SITE}/")
if __name__ == "__main__":
main()
📄 NEW FILE · check1.py
"""check1.py — read your own pages the way a search engine will."""
import html
import re
import sys
from collections import Counter
from pathlib import Path
FOLDER = Path("site")
# Google shows roughly this much. Longer is not an error; it is just cut off
# in the results, and the part it cuts is the part you wrote last.
TITLE_ROOM = 60
DESCRIPTION_ROOM = 155
sys.stdout.reconfigure(encoding="utf-8")
def first(pattern, text):
"""The first capture of a pattern, unescaped, or None."""
found = re.search(pattern, text, re.I | re.S)
return html.unescape(found.group(1)).strip() if found else None
def read(path):
"""Pull out the handful of things a search engine actually reads."""
text = path.read_text(encoding="utf-8")
return {
"title": first(r"<title>(.*?)</title>", text),
"description": first(r'<meta\s+name="description"\s+content="(.*?)"', text),
"lang": first(r"<html[^>]*\blang=\"([^\"]+)\"", text),
"h1": first(r"<h1[^>]*>(.*?)</h1>", text),
"noindex": bool(re.search(r'name="robots"[^>]*noindex', text, re.I)),
}
def main():
pages = {path: read(path) for path in sorted(FOLDER.rglob("*.html"))}
# Two pages with the same title look like one page to a search engine,
# and it picks which to show. That choice is not usually yours.
titles = Counter(page["title"] for page in pages.values() if page["title"])
problems = 0
for path, page in pages.items():
found = []
if not page["title"]:
found.append("no title")
elif titles[page["title"]] > 1:
found.append(f"title shared with {titles[page['title']] - 1} other")
elif len(page["title"]) > TITLE_ROOM:
found.append(f"title is {len(page['title'])} characters, "
f"{TITLE_ROOM} get shown")
if not page["description"]:
found.append("no description, so Google writes one for you")
elif len(page["description"]) > DESCRIPTION_ROOM:
found.append(f"description is {len(page['description'])} characters")
if not page["lang"]:
found.append("no lang on <html>")
if not page["h1"]:
found.append("no <h1>")
name = path.relative_to(FOLDER).as_posix()
if found:
problems = problems + len(found)
print(f"{name}")
for trouble in found:
print(f" - {trouble}")
else:
print(f"{name} ok")
print()
print(f"{len(pages)} pages, {problems} things to fix")
if __name__ == "__main__":
main()
| Line | What it does · ทำอะไร · 做什么 |
|---|---|
| TITLE_ROOM = 60 | Roughly what Google shows. Longer is cut, and it cuts the end.ประมาณที่ Google แสดง ถ้ายาวกว่าจะถูกตัด และตัดท้าย大致是 Google 显示的量。更长会被截掉,截的是后面。 |
| titles = Counter(page["title"] | Two pages with one title look like one page. Google picks which.สองหน้าที่ใช้ชื่อเดียวกันดูเหมือนหน้าเดียว Google เป็นคนเลือกว่าจะโชว์อันไหน两个页面同一个标题,看起来就是一个页面。Google 替你选。 |
| "no description, so Google writes one for you" | It will take a sentence off the page. Usually not the one you want.มันจะหยิบประโยคจากหน้านั้นมาใช้ ซึ่งมักไม่ใช่ประโยคที่คุณอยากได้它会从页面上抓一句。通常不是你想要的那句。 |
🖥️ WHAT IT PRINTED · on the practice site
drafts/notes.html ok
index.html ok
khao-soi.html
- title shared with 1 other
- no description, so Google writes one for you
python.html
- title shared with 1 other
4 pages, 3 things to fix
- Run both files. Read the three complaints.
- Fix the title on
khao-soi.htmland run it again. - Point
FOLDERat your own site. - Fix whatever it finds before going on.
- รันทั้งสองไฟล์ แล้วอ่านคำบ่นทั้งสามข้อ
- แก้ชื่อของ
khao-soi.htmlแล้วรันใหม่ - ชี้
FOLDERไปที่เว็บของคุณเอง - แก้ทุกอย่างที่มันเจอก่อนไปต่อ
- 两个文件都跑一遍,读那三条抱怨。
- 把
khao-soi.html的标题改掉,再跑一次。 - 把
FOLDER指向你自己的站。 - 在继续之前,把它找到的都修掉。
🗺️ Tutorial 2 — Tell it where everything is · บทที่ 2 — บอกมันว่าอะไรอยู่ตรงไหน · 教程 2 —— 告诉它东西都在哪
🇬🇧 English
Two small files do most of the work: a list of your pages, and a note saying where that list lives.
🇹🇭 ไทย
ไฟล์เล็ก ๆ สองไฟล์ทำงานเกือบทั้งหมด คือรายชื่อหน้าเว็บของคุณ กับบันทึกที่บอกว่ารายชื่อนั้นอยู่ที่ไหน
🇨🇳 中文
两个小文件干了大部分的活:一份你的页面清单,和一张写着清单在哪的纸条。
3 A sitemap, and a robots.txt that points at it · sitemap กับ robots.txt ที่ชี้ไปหามัน · 一份 sitemap,和一个指向它的 robots.txt
🇬🇧 English
A sitemap is a list of the pages you want found. Not every page: the ones you are asking for.
Note what the script leaves out, and what it does with index.html.
🇹🇭 ไทย
sitemap คือรายชื่อหน้าที่คุณอยากให้ถูกค้นเจอ ไม่ใช่ทุกหน้า แต่คือหน้าที่คุณกำลังขอ
สังเกตว่าสคริปต์ตัดอะไรออก และทำอะไรกับ index.html
🇨🇳 中文
sitemap 是一份你希望被找到的页面清单。不是所有页面,是你要求的那些。
注意脚本排除了什么,以及它怎么处理 index.html。
📄 NEW FILE · sitemap1.py
"""sitemap1.py — write the list of pages you want Google to know about."""
import sys
import time
from pathlib import Path
# Your site's address. The trailing slash matters; every URL is built on it.
SITE = "https://nari.github.io/"
FOLDER = Path("site")
# Folders whose pages should not be in the sitemap. A sitemap is a list of
# pages you are asking to have indexed, so drafts do not belong in it.
SKIP = {"drafts"}
sys.stdout.reconfigure(encoding="utf-8")
def pages():
"""Every .html file in the folder, as a web address, shortest first."""
found = []
for path in sorted(FOLDER.rglob("*.html")):
parts = path.relative_to(FOLDER).parts
if SKIP & set(parts[:-1]):
continue
# index.html is the folder itself. Listing both is one page twice.
where = "/".join(parts[:-1] + ((), (parts[-1],))[parts[-1] != "index.html"])
found.append((SITE + where, path))
return found
def main():
lines = ['<?xml version="1.0" encoding="UTF-8"?>',
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">']
for url, path in pages():
# The date the file changed. Google reads it as a hint, not a promise.
when = time.strftime("%Y-%m-%d", time.gmtime(path.stat().st_mtime))
lines.append(f" <url><loc>{url}</loc><lastmod>{when}</lastmod></url>")
print(f" {url}")
lines.append("</urlset>")
target = FOLDER / "sitemap.xml"
target.write_text("\n".join(lines) + "\n", encoding="utf-8")
# robots.txt is how a search engine finds the sitemap without being told.
robots = FOLDER / "robots.txt"
robots.write_text("User-agent: *\nAllow: /\n\n"
f"Sitemap: {SITE}sitemap.xml\n", encoding="utf-8")
print(f"{len(lines) - 3} pages -> {target}")
print(f"and {robots}")
if __name__ == "__main__":
main()
| Line | What it does · ทำอะไร · 做什么 |
|---|---|
| SKIP = {"drafts"} | A sitemap is a request. Do not request the pages you are hiding.sitemap คือคำขอ อย่าขอหน้าที่คุณกำลังซ่อนอยู่sitemap 是一个请求。别去请求你正在藏的页面。 |
| parts[-1] != "index.html" | A folder and its index.html are one page. List it once.โฟลเดอร์กับ index.html ของมันคือหน้าเดียวกัน ให้ใส่แค่ครั้งเดียว一个文件夹和它的 index.html 是同一个页面。只列一次。 |
| Sitemap: {SITE}sitemap.xml | This line is how a crawler finds the list without being told.บรรทัดนี้คือวิธีที่ตัวเก็บข้อมูลหาเจอรายชื่อโดยไม่ต้องมีใครบอก这一行是爬虫在没人告诉它的情况下找到清单的办法。 |
🖥️ WHAT IT PRINTED · three pages, one skipped
https://nari.github.io/
https://nari.github.io/khao-soi.html
https://nari.github.io/python.html
3 pages -> site\sitemap.xml
and site\robots.txt
about/index.html into /about/. GitHub Pages serves that, which microsoft.github.io/monaco-editor/ confirms. Other hosts do not. This site is S3 behind CloudFront. There /io2026/ is a 404 and /io2026/index.html is a 200. Challenge 1 checks yours rather than trusting either of us.
สคริปต์นี้เปลี่ยน about/index.html ให้เป็น /about/ GitHub Pages ให้บริการแบบนั้นได้ ซึ่ง microsoft.github.io/monaco-editor/ ยืนยันแล้ว แต่โฮสต์อื่นทำไม่ได้ บนเว็บนี้ซึ่งเป็น S3 หลัง CloudFront /io2026/ ได้ 404 ส่วน /io2026/index.html ได้ 200 โจทย์ข้อ 1 จะตรวจของคุณเอง แทนที่จะเชื่อเราสองคน
这个脚本把 about/index.html 变成 /about/。GitHub Pages 支持这样,microsoft.github.io/monaco-editor/ 可以证明。别的主机不一定。在这个站上(S3 加 CloudFront),/io2026/ 是 404,而 /io2026/index.html 是 200。第 1 题会去查你自己的,而不是听我们任何一方的。
- Run it. Open
site/sitemap.xmland read it. - Notice
drafts/notes.htmlis not in there. - Add your own site's address to
SITE. - Commit both files and push.
- รันดู แล้วเปิด
site/sitemap.xmlอ่าน - สังเกตว่า
drafts/notes.htmlไม่อยู่ในนั้น - ใส่ที่อยู่เว็บของคุณเองลงใน
SITE - คอมมิตทั้งสองไฟล์แล้วพุช
- 运行,打开
site/sitemap.xml读一读。 - 注意
drafts/notes.html不在里面。 - 把你自己站的地址填进
SITE。 - 两个文件都提交并推上去。
4 Check it as it is actually served · ตรวจมันในแบบที่ถูกเสิร์ฟจริง · 按它真正被送出的样子检查
🇬🇧 English
Everything so far read files on your disk. Only the served version counts.
Run it on your site, and on a site that has none of this, and the difference is the whole point.
🇹🇭 ไทย
ที่ผ่านมาทั้งหมดคืออ่านไฟล์บนดิสก์ แต่เวอร์ชันที่ถูกเสิร์ฟออกไปเท่านั้นที่นับ
รันกับเว็บของคุณ แล้วรันกับเว็บที่ไม่มีอะไรพวกนี้เลย ความต่างนั่นแหละคือประเด็นทั้งหมด
🇨🇳 中文
到目前为止都是在读硬盘上的文件。只有被送出去的那个版本算数。
在你的站上跑一次,再在一个什么都没有的站上跑一次,那个差别就是全部的重点。
📄 NEW FILE · live1.py
"""live1.py — check the site the way Google will, over the network.
Everything up to here read files on your disk. This reads the site as it
is actually served, which is the only version that counts.
"""
import re
import sys
import urllib.error
import urllib.request
SITE = sys.argv[1] if len(sys.argv) > 1 else "https://nari.github.io/"
if not SITE.endswith("/"):
SITE = SITE + "/"
sys.stdout.reconfigure(encoding="utf-8")
def fetch(url):
"""Get a page, and the headers, without pretending to be a browser."""
request = urllib.request.Request(url, headers={"User-Agent": "a lesson"})
try:
with urllib.request.urlopen(request, timeout=30) as response:
return response.status, dict(response.headers), response.read()
except urllib.error.HTTPError as refused:
return refused.code, dict(refused.headers), b""
except urllib.error.URLError as trouble:
print("cannot reach", url, "-", trouble.reason)
raise SystemExit(1)
def main():
print("checking", SITE)
status, headers, body = fetch(SITE)
print(f" the front page {status}")
# This header can hide a whole site from search, and nothing on the
# page shows it. It is the first thing to rule out.
blocked = headers.get("X-Robots-Tag", "")
print(f" X-Robots-Tag {blocked or 'not set, good'}")
status, _, robots = fetch(SITE + "robots.txt")
print(f" robots.txt {status}")
sitemap_line = ""
if status == 200:
text = robots.decode("utf-8", errors="replace")
if re.search(r"^\s*Disallow:\s*/\s*$", text, re.M | re.I):
print(" Disallow: / is in there. That hides everything.")
found = re.search(r"^\s*Sitemap:\s*(\S+)", text, re.M | re.I)
sitemap_line = found.group(1) if found else ""
print(f" names a sitemap {sitemap_line or 'no'}")
target = sitemap_line or SITE + "sitemap.xml"
status, _, xml = fetch(target)
print(f" sitemap.xml {status}")
if status != 200:
return
urls = re.findall(r"<loc>\s*([^<\s]+)\s*</loc>",
xml.decode("utf-8", errors="replace"))
print(f" lists {len(urls)} pages")
# A sitemap full of dead links is worse than no sitemap. Check a few.
bad = []
for url in urls[:12]:
code, _, _ = fetch(url)
if code != 200:
bad.append((code, url))
print(f" checked {min(len(urls), 12)}, "
f"{len(bad)} not reachable")
for code, url in bad:
print(f" {code} {url}")
if __name__ == "__main__":
main()
🖥️ A SITE WITH ALL OF IT · run against a real one
checking https://krueng.ai/
the front page 200
X-Robots-Tag not set, good
robots.txt 200
names a sitemap https://krueng.ai/sitemap.xml
sitemap.xml 200
lists 113 pages
checked 12, 0 not reachable
🖥️ AND A BARE GITHUB PAGES SITE · the same script
checking https://octocat.github.io/
the front page 200
X-Robots-Tag not set, good
robots.txt 404
sitemap.xml 404
| Line | What it does · ทำอะไร · 做什么 |
|---|---|
| re.search(r"^\s*Disallow:\s*/\s*$" | One line in robots.txt can hide everything. Look for it by name.บรรทัดเดียวใน robots.txt ซ่อนได้ทุกอย่าง ให้ตามหามันตรง ๆrobots.txt 里一行就能把一切藏起来。直接按名字去找它。 |
| for url in urls[:12]: | A sitemap of dead links teaches Google to stop reading it.sitemap ที่เต็มไปด้วยลิงก์ตายสอนให้ Google เลิกอ่านมัน一份全是死链的 sitemap,会教会 Google 别再读它。 |
- Run it on your own site. Everything should be 200.
- Run it on
https://octocat.github.io/for contrast. - Add
Disallow: /to your robots.txt and rerun. - Take it out again. Now you know what that looks like.
- รันกับเว็บของคุณเอง ทุกอย่างควรได้ 200
- รันกับ
https://octocat.github.io/เพื่อเทียบ - ใส่
Disallow: /ลงใน robots.txt แล้วรันใหม่ - แล้วเอาออก ทีนี้คุณรู้แล้วว่าหน้าตามันเป็นยังไง
- 在你自己的站上跑,应该全是 200。
- 再在
https://octocat.github.io/上跑一次做对比。 - 往 robots.txt 里加一行
Disallow: /,再跑。 - 然后删掉。现在你知道那长什么样了。
🔎 Tutorial 3 — Search Console itself · บทที่ 3 — ตัว Search Console เอง · 教程 3 —— Search Console 本身
🇬🇧 English
Two jobs here, and they are both done in a browser. Prove the site is yours, then hand over the sitemap.
The first one has a catch specific to github.io.
🇹🇭 ไทย
ตรงนี้มีสองงาน และทั้งคู่ทำในเบราว์เซอร์ คือพิสูจน์ว่าเว็บเป็นของคุณ แล้วส่ง sitemap ให้
งานแรกมีข้อติดที่เป็นเฉพาะของ github.io
🇨🇳 中文
这里有两件事,都在浏览器里做:证明这个站是你的,然后把 sitemap 交上去。
第一件对 github.io 来说有个坎。
5 Proving the site is yours · พิสูจน์ว่าเว็บเป็นของคุณ · 证明这个站是你的
🇬🇧 English
Go to search.google.com/search-console and add a property. You are offered two kinds, and for a github.io address only one of them can work.
🇹🇭 ไทย
ไปที่ search.google.com/search-console แล้วเพิ่ม property มันจะให้เลือกสองแบบ และสำหรับที่อยู่ github.io มีแบบเดียวเท่านั้นที่ใช้ได้
🇨🇳 中文
去 search.google.com/search-console 添加一个资源。它给你两种选择,而对 github.io 地址来说只有一种行得通。
| Domain propertyแบบ Domain网域资源 | URL prefix propertyแบบ URL prefix网址前缀资源 | |
|---|---|---|
| What it coversครอบคลุมอะไร覆盖什么 | Domain: every subdomain, http and httpsDomain: ทุกซับโดเมน ทั้ง http และ https网域:所有子域名,http 和 https | URL prefix: exactly that addressURL prefix: เฉพาะที่อยู่นั้นเป๊ะ ๆ网址前缀:就那一个地址 |
| How you prove itพิสูจน์ยังไง怎么证明 | Domain: a DNS record, only thatDomain: ต้องใช้ DNS record เท่านั้น网域:只能用 DNS 记录 | URL prefix: a file, a meta tag, and othersURL prefix: ใช้ไฟล์ แท็ก meta หรือวิธีอื่น网址前缀:文件、meta 标签,还有别的 |
| On yourname.github.ioบน yourname.github.io在 yourname.github.io 上 | Domain: impossible, you own no DNS thereDomain: ทำไม่ได้ คุณไม่ได้เป็นเจ้าของ DNS ตรงนั้น网域:做不到,那里的 DNS 不归你 | URL prefix: this is the oneURL prefix: ใช้อันนี้แหละ网址前缀:就用这个 |
💻 THE FILE METHOD · the one that always works on Pages
# Search Console gives you a file named something like:
# google1a2b3c4d5e6f7890.html
# Put it at the top of the repository, not in a folder.
git add google1a2b3c4d5e6f7890.html
git commit -m 'search console'
git push
# Wait for the build, then check it yourself before clicking Verify:
curl -I https://yourname.github.io/google1a2b3c4d5e6f7890.html
curl anyway. A failed verification is slower to undo than a ten second check.
มีสองอย่างที่กินไฟล์นั้นไป อย่างแรกคือการพุชที่ยังบิลด์ไม่เสร็จ อย่างที่สองคือ Jekyll ซึ่ง GitHub Pages รันให้โดยค่าเริ่มต้น และมันจะข้ามไฟล์กับโฟลเดอร์ที่ชื่อขึ้นต้นด้วยขีดล่าง ไฟล์ที่ Google ให้มาไม่ได้ขึ้นต้นด้วยขีดล่าง จึงรอด แต่ก็ควรเช็กด้วย curl อยู่ดี การยืนยันที่ล้มเหลวแก้ยากกว่าการเช็กสิบวินาที
有两样东西会吃掉那个文件:一是还没构建完的推送,二是 GitHub Pages 默认会跑的 Jekyll,它会跳过名字以下划线开头的文件和文件夹。Google 给你的文件不是以下划线开头的,所以它没事。但还是用 curl 查一下。验证失败比十秒钟的检查麻烦多了。
- Open Search Console and choose URL prefix.
- Type your full address, with https and the trailing slash.
- Download the HTML file, commit it, push it.
curl -Iit. Only then click Verify.
- เปิด Search Console แล้วเลือกแบบ URL prefix
- พิมพ์ที่อยู่เต็มของคุณ พร้อม https และเครื่องหมายทับท้าย
- ดาวน์โหลดไฟล์ HTML คอมมิต แล้วพุช
curl -Iดูก่อน แล้วค่อยกด Verify
- 打开 Search Console,选网址前缀。
- 输入你的完整地址,带 https 和结尾的斜杠。
- 下载那个 HTML 文件,提交,推上去。
- 先
curl -I一下。然后再点验证。
6 Handing over the sitemap, and waiting · ส่ง sitemap ให้ แล้วรอ · 把 sitemap 交上去,然后等
🇬🇧 English
Sitemaps, paste sitemap.xml, submit. It should say Success within a minute or two.
Then nothing happens for days, and that is the part people get wrong rather than the setup.
🇹🇭 ไทย
ไปที่ Sitemaps วาง sitemap.xml แล้วกดส่ง มันควรขึ้นว่า Success ภายในหนึ่งสองนาที
จากนั้นจะไม่มีอะไรเกิดขึ้นเป็นวัน ๆ และนั่นคือส่วนที่คนเข้าใจผิด มากกว่าเรื่องการตั้งค่า
🇨🇳 中文
进 Sitemaps,粘贴 sitemap.xml,提交。一两分钟内应该显示 Success。
然后好几天什么都不会发生,而人们弄错的通常是这一点,不是设置。
| What it saysมันบอกว่า它说什么 | What it meansแปลว่าอะไร什么意思 |
|---|---|
| SuccessSuccessSuccess | read the file, found the URLs. Not indexed yetอ่านไฟล์แล้ว เจอ URL แล้ว แต่ยังไม่ได้จัดทำดัชนี读到了文件,找到了网址。还没被收录 |
| Couldn't fetchCouldn't fetch无法获取 | wrong address, or not built yet. Check with curlที่อยู่ผิด หรือยังบิลด์ไม่เสร็จ ให้เช็กด้วย curl地址错了,或者还没构建完。用 curl 查 |
| Discovered, not indexedDiscovered, not indexed已发现,尚未编入索引 | it knows. It has not decided you are worth itมันรู้แล้ว แต่ยังไม่ตัดสินใจว่าคุณคุ้มค่าพอ它知道了。但还没决定你值不值 |
| Crawled, not indexedCrawled, not indexed已抓取,尚未编入索引 | it read the page and passed. Usually thin or duplicateมันอ่านหน้านั้นแล้วผ่าน มักเพราะเนื้อหาบางหรือซ้ำ它读了这一页然后跳过。通常是内容太薄或重复 |
google.com/ping?sitemap=... after every change. That address now answers 404, measured while writing this. Google removed it and reads sitemaps on its own schedule. Bing and Yandex kept a way in, and that is challenge 4.
คู่มือหลายที่ยังบอกให้เรียก google.com/ping?sitemap=... ทุกครั้งที่มีการเปลี่ยนแปลง ตอนนี้ที่อยู่นั้นตอบ 404 ซึ่งวัดตอนเขียนหน้านี้ Google เอามันออกไปแล้ว และอ่าน sitemap ตามตารางของตัวเอง ส่วน Bing กับ Yandex ยังเปิดทางไว้ นั่นคือโจทย์ข้อ 4
很多指南还告诉你每次改动后去请求 google.com/ping?sitemap=...。那个地址现在返回 404,写这一页时实测过。Google 把它撤了,改成按自己的节奏读 sitemap。Bing 和 Yandex 还留了一条路,那是第 4 题。
- Submit the sitemap. Check it says Success.
- Use URL Inspection on your front page. Request indexing, once.
- Close the tab. Come back in three days, not three hours.
- Then read Pages, and see which state your pages are in.
- ส่ง sitemap แล้วเช็กว่าขึ้น Success
- ใช้ URL Inspection กับหน้าแรก แล้วขอให้จัดทำดัชนี หนึ่งครั้ง
- ปิดแท็บไปเลย กลับมาอีกทีในสามวัน ไม่ใช่สามชั่วโมง
- แล้วค่อยอ่านหน้า Pages ดูว่าหน้าเว็บของคุณอยู่ในสถานะไหน
- 提交 sitemap,确认它显示 Success。
- 对首页用一次网址检查,请求编入索引,只一次。
- 关掉标签页。三天后再来,不是三小时。
- 然后看 Pages 那一栏,看你的页面处在哪个状态。
🏆 Tutorial 4 — Four challenges · บทที่ 4 — โจทย์สี่ข้อ · 教程 4 —— 四道题
🇬🇧 English
The first two find problems Google would find for you, slowly. The last two save you from doing this by hand.
🇹🇭 ไทย
สองข้อแรกหาปัญหาที่ Google จะหาเจอให้คุณเองแบบช้า ๆ ส่วนสองข้อหลังช่วยไม่ให้คุณต้องทำมือ
🇨🇳 中文
前两道找的是 Google 迟早会替你找到的问题。后两道省掉你手工做这些事。
7 Try first. The answers are folded away. · ลองเองก่อน เฉลยพับซ่อนไว้ · 先自己试。答案折起来放着。
🇬🇧 English
Every one of these was run before it was written down.
🇹🇭 ไทย
ทุกข้อในนี้ถูกรันจริงก่อนจะถูกเขียนลงมา
🇨🇳 中文
这里每一道都是先跑过才写下来的。
1Never submit a page that is not there · อย่าส่งหน้าที่ไม่มีอยู่จริง · 别提交一个并不存在的页面
🇬🇧 English
sitemap1.py lists every file it finds. A file on your disk is not the same as a page on your site.
Check each address over the network before you put it in the list.
🇹🇭 ไทย
sitemap1.py ใส่ทุกไฟล์ที่มันเจอ แต่ไฟล์บนดิสก์ไม่ใช่สิ่งเดียวกับหน้าบนเว็บ
ตรวจแต่ละที่อยู่ผ่านเน็ตก่อนจะเอาใส่ลงในรายชื่อ
🇨🇳 中文
sitemap1.py 把找到的文件全列上。硬盘上的文件不等于站上的页面。
在放进清单之前,先联网查一下每个地址。
Show the answerดูเฉลย看答案
📄 verify1.py
"""verify1.py — challenge 1: never submit a page that is not there.
sitemap1.py lists every file it finds. A file on your disk is not the same
as a page on your site: the name can differ by a capital letter, the push
can have failed, the build can have skipped it. Check first, list second.
"""
import sys
import time
import urllib.error
import urllib.request
from pathlib import Path
# Point it at your own site and the folder you publish from.
# python verify1.py https://nari.github.io/ site
SITE = sys.argv[1] if len(sys.argv) > 1 else "https://nari.github.io/"
FOLDER = Path(sys.argv[2] if len(sys.argv) > 2 else "site")
SKIP = {"drafts"}
sys.stdout.reconfigure(encoding="utf-8")
def addresses():
"""The same list sitemap1.py would write."""
found = []
for path in sorted(FOLDER.rglob("*.html")):
parts = path.relative_to(FOLDER).parts
if SKIP & set(parts[:-1]):
continue
tail = "/".join(parts[:-1] + ((), (parts[-1],))[parts[-1] != "index.html"])
found.append(SITE + tail)
return found
def answers(url):
"""Does this address really return a page? Follow nothing, forgive nothing."""
request = urllib.request.Request(url, method="HEAD",
headers={"User-Agent": "a lesson"})
try:
with urllib.request.urlopen(request, timeout=20) as response:
return response.status
except urllib.error.HTTPError as refused:
return refused.code
except urllib.error.URLError:
return 0
def main():
good, bad = [], []
for url in addresses():
code = answers(url)
(good if code == 200 else bad).append((code, url))
print(f" {code or 'dead':>4} {url}")
time.sleep(0.2) # be polite to your own server
print()
print(f"{len(good)} would go in the sitemap, {len(bad)} would not")
if bad:
print("Fix these or drop them. A sitemap of 404s teaches Google")
print("that your sitemap is not worth reading.")
if __name__ == "__main__":
main()
🖥️ WHAT IT FOUND · 499 pages of a real site
python verify1.py https://krueng.ai/ ../HTML
496 would go in the sitemap, 3 would not
404 https://krueng.ai/games/mueang-lanna
404 https://krueng.ai/io2026
404 https://krueng.ai/it/ai/plans
🇬🇧 English
All three are folders with an index.html inside. This host does not serve those.
That is the careful box from step 3, found by a script instead of by a reader.
🇹🇭 ไทย
ทั้งสามอันเป็นโฟลเดอร์ที่มี index.html อยู่ข้างใน โฮสต์นี้ไม่ได้ให้บริการแบบนั้น
นั่นคือกล่องคำเตือนจากขั้นที่ 3 แต่คราวนี้สคริปต์เป็นคนเจอ ไม่ใช่คนอ่าน
🇨🇳 中文
这三个都是里面放着 index.html 的文件夹。这个主机不提供那种服务。
就是第 3 步那个警告框,只不过这次是脚本发现的,不是读者。
2Find the pages nothing links to · หาหน้าที่ไม่มีอะไรลิงก์ไปหา · 找出没有任何链接指向的页面
🇬🇧 English
Google follows links. A page in your sitemap that nothing points at is one it reaches last and trusts least.
Usually it is a page you forgot, not one you meant to hide.
🇹🇭 ไทย
Google เดินตามลิงก์ หน้าที่อยู่ใน sitemap แต่ไม่มีอะไรชี้ไปหา คือหน้าที่มันไปถึงช้าที่สุดและเชื่อถือน้อยที่สุด
ส่วนใหญ่มันคือหน้าที่คุณลืม ไม่ใช่หน้าที่คุณตั้งใจซ่อน
🇨🇳 中文
Google 跟着链接走。sitemap 里一个没人指向的页面,它最后才到、最不信任。
通常那是你忘了的页面,不是你想藏的。
Show the answerดูเฉลย看答案
📄 orphans1.py
"""orphans1.py — challenge 2: find the pages nothing links to.
Google follows links. A page in your sitemap that no other page points at
is a page it will reach last and trust least, and usually it is a page you
forgot about rather than one you meant to hide.
"""
import re
import sys
from pathlib import Path
FOLDER = Path("site")
sys.stdout.reconfigure(encoding="utf-8")
def linked_from(path):
"""Every local page this one points at, as a path inside the folder."""
text = path.read_text(encoding="utf-8")
here = path.parent
out = set()
for href in re.findall(r'<a[^>]+href="([^"]+)"', text, re.I):
# Leave the outside world, anchors and mail links alone.
if href.startswith(("http://", "https://", "#", "mailto:", "//")):
continue
target = (here / href.split("#")[0].split("?")[0]).resolve()
out.add(target)
return out
def main():
pages = sorted(FOLDER.rglob("*.html"))
pointed_at = set()
for path in pages:
pointed_at |= linked_from(path)
front = (FOLDER / "index.html").resolve()
orphans = [p for p in pages
if p.resolve() not in pointed_at and p.resolve() != front]
print(f"{len(pages)} pages, {len(pointed_at)} link targets found")
print()
if not orphans:
print("nothing is orphaned")
return
print("nothing links to these:")
for path in orphans:
print(" ", path.relative_to(FOLDER).as_posix())
print()
print("Either link to them from a page that is already indexed,")
print("or take them out of the sitemap. Do not do neither.")
if __name__ == "__main__":
main()
🖥️ WHAT IT PRINTED · on the practice site
4 pages, 1 link targets found
nothing links to these:
drafts/notes.html
khao-soi.html
Either link to them from a page that is already indexed,
or take them out of the sitemap. Do not do neither.
| Line | What it does · ทำอะไร · 做什么 |
|---|---|
| if href.startswith(("http://", "https://", "#", "mailto:" | Only local links count. An outside link is not your structure.นับเฉพาะลิงก์ภายใน ลิงก์ออกข้างนอกไม่ใช่โครงสร้างของคุณ只算站内链接。往外的链接不是你的结构。 |
| p.resolve() != front | The front page is the way in. Nothing has to link to it.หน้าแรกคือทางเข้า ไม่จำเป็นต้องมีอะไรลิงก์ไปหามัน首页就是入口。不需要任何东西链接到它。 |
3Rebuild the sitemap on every push · สร้าง sitemap ใหม่ทุกครั้งที่พุช · 每次推送都重建 sitemap
🇬🇧 English
A sitemap you regenerate by hand is a sitemap that is one page out of date.
GitHub can run the script for you and commit the result.
🇹🇭 ไทย
sitemap ที่ต้องสร้างใหม่ด้วยมือ คือ sitemap ที่ล้าหลังอยู่หนึ่งหน้าเสมอ
GitHub รันสคริปต์ให้คุณได้ แล้วคอมมิตผลลัพธ์ให้ด้วย
🇨🇳 中文
靠手工重建的 sitemap,总是慢一个页面。
GitHub 可以替你跑那个脚本,还能把结果提交上去。
Show the answerดูเฉลย看答案
📄 .github/workflows/sitemap.yml
# .github/workflows/sitemap.yml
# Challenge 3: rebuild the sitemap every time you push, so it is never
# one page behind the site.
name: Rebuild the sitemap
on:
push:
branches: [main]
# A workflow can read the repository by default. Writing back to it needs
# saying so here, and this one commits the sitemap it just built.
permissions:
contents: write
jobs:
sitemap:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Write sitemap.xml and robots.txt
run: python sitemap1.py
- name: Commit them, but only if they changed
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add site/sitemap.xml site/robots.txt
# --quiet returns non-zero when there is something staged, so this
# commits only when there is a real change. Without it the job
# fails on every push that did not touch a page.
git diff --staged --quiet || git commit -m "sitemap: rebuild"
git push
| Line | What it does · ทำอะไร · 做什么 |
|---|---|
| permissions: | A workflow can read the repository. Writing needs saying so.เวิร์กโฟลว์อ่านรีโพได้อยู่แล้ว แต่การเขียนต้องระบุไว้工作流默认能读仓库。要写就得写明。 |
| git diff --staged --quiet || git commit | Commit only when something changed, or every push fails.คอมมิตเฉพาะตอนที่มีอะไรเปลี่ยน ไม่งั้นทุกการพุชจะล้มเหลว只在有变化时提交,否则每次推送都会失败。 |
4Tell Bing the moment something changes · บอก Bing ทันทีที่มีอะไรเปลี่ยน · 东西一变就告诉 Bing
🇬🇧 English
Google's ping endpoint is gone. Bing and Yandex share one called IndexNow, and it is two steps.
Put a key file on the site, then name the pages that changed. Check the key file is live before sending.
🇹🇭 ไทย
ปลายทางสำหรับ ping ของ Google หายไปแล้ว ส่วน Bing กับ Yandex ใช้ตัวเดียวกันชื่อ IndexNow และมีสองขั้นตอน
วางไฟล์คีย์ไว้บนเว็บ แล้วบอกชื่อหน้าที่เปลี่ยน ตรวจว่าไฟล์คีย์ใช้งานได้จริงก่อนส่ง
🇨🇳 中文
Google 的 ping 端点没了。Bing 和 Yandex 共用一个叫 IndexNow 的,分两步。
把一个 key 文件放到站上,再报出变了的页面。发送前先确认 key 文件是活的。
Show the answerดูเฉลย看答案
📄 tell1.py
"""tell1.py — tell a search engine a page changed, without waiting.
Google used to take a ping at /ping?sitemap=. That endpoint now answers
404; they removed it and say they read sitemaps on their own schedule.
Bing and Yandex kept a way in, called IndexNow, and it is two steps: put
a key file on your site, then name the pages that changed.
"""
import json
import secrets
import sys
import urllib.error
import urllib.request
from pathlib import Path
SITE = "https://nari.github.io/"
FOLDER = Path("site")
INDEXNOW = "https://api.indexnow.org/indexnow"
sys.stdout.reconfigure(encoding="utf-8")
def key_file():
"""Make the key once and keep it. It lives in the site, so it is public."""
saved = FOLDER / "indexnow-key.txt"
if saved.exists():
return saved.read_text(encoding="utf-8").strip()
# Any 8 to 128 hex characters. It proves you can write to the site,
# which is the only thing it needs to prove.
key = secrets.token_hex(16)
saved.write_text(key, encoding="utf-8")
(FOLDER / (key + ".txt")).write_text(key, encoding="utf-8")
print("made a new key and wrote", key + ".txt")
return key
def live(url):
"""Is this address actually reachable? Ask before claiming anything."""
try:
with urllib.request.urlopen(url, timeout=20) as response:
return response.status == 200
except (urllib.error.URLError, urllib.error.HTTPError):
return False
def main():
key = key_file()
proof = SITE + key + ".txt"
print("key file should be at", proof)
if not live(proof):
# Sending before the key file is published gets the whole request
# rejected, and the reason is not obvious from the error.
print("that address does not answer yet.")
print("Commit the key file, push, wait for Pages to build, then rerun.")
return
changed = [SITE, SITE + "python.html"]
body = json.dumps({"host": SITE.split("/")[2], "key": key,
"urlList": changed}).encode()
request = urllib.request.Request(
INDEXNOW, data=body, headers={"Content-Type": "application/json"})
try:
with urllib.request.urlopen(request, timeout=30) as response:
print("told IndexNow about", len(changed), "pages:", response.status)
except urllib.error.HTTPError as refused:
# 422 means it found the key file but did not like something.
print("refused:", refused.code, refused.read().decode()[:200])
if __name__ == "__main__":
main()
🖥️ WHAT IT PRINTED · before the key file was published
made a new key and wrote b44cba0f1f6002f53edc1da1eea9a2e1.txt
key file should be at
https://nari.github.io/b44cba0f1f6002f53edc1da1eea9a2e1.txt
that address does not answer yet.
Commit the key file, push, wait for Pages to build, then rerun.
| Line | What it does · ทำอะไร · 做什么 |
|---|---|
| if not live(proof): | Sending before the key is published fails, unhelpfully.ถ้าส่งก่อนที่คีย์จะถูกเผยแพร่ มันจะล้มเหลวแบบไม่บอกเหตุผล在 key 发布之前发送会失败,而且不告诉你为什么。 |
| secrets.token_hex(16) | The key is public. It only proves you can write to the site.คีย์นี้เป็นสาธารณะ มันแค่พิสูจน์ว่าคุณเขียนลงเว็บได้这个 key 是公开的。它只证明你能往这个站写东西。 |
🇬🇧 English
Eight files, and most of the work is two small text files and a browser tab.
The part worth remembering is the order. Make the pages readable, list them, check the list as served, then ask.
Asking first is what everybody does, and it is why they sit refreshing a page that says Discovered.
🇹🇭 ไทย
แปดไฟล์ และงานส่วนใหญ่คือไฟล์ข้อความเล็ก ๆ สองไฟล์กับแท็บเบราว์เซอร์หนึ่งแท็บ
สิ่งที่ควรจำคือลำดับ ทำให้หน้าอ่านได้ก่อน แล้วทำรายชื่อ แล้วตรวจรายชื่อในแบบที่ถูกเสิร์ฟ แล้วค่อยขอ
การขอก่อนคือสิ่งที่ทุกคนทำ และนั่นคือเหตุผลที่พวกเขานั่งกดรีเฟรช หน้าที่เขียนว่า Discovered
🇨🇳 中文
八个文件,而大部分的活就是两个小文本文件和一个浏览器标签页。
值得记住的是顺序:先让页面读得懂,再列清单,再按被送出的样子检查清单,最后才去要。
先去要,是所有人都会做的事,也是他们对着写着「已发现」的页面一直刷新的原因。
| You wantคุณอยากได้你想要 | Look upไปหาคำว่า去查 |
|---|---|
| Your own domain on Pagesใช้โดเมนตัวเองกับ Pages在 Pages 上用自己的域名 | a CNAME file, then a Domain propertyไฟล์ CNAME แล้วค่อยใช้ property แบบ Domain一个 CNAME 文件,然后用网域资源 |
| Which searches found youคนค้นอะไรถึงเจอคุณ人们搜什么找到你 | the Performance report, after a weekรายงาน Performance หลังผ่านไปหนึ่งสัปดาห์一周后看效果报告 |
| A page to stay outไม่อยากให้หน้าไหนถูกเก็บ让某一页别被收录 | <meta name="robots" content="noindex"> |
| Bing's own consoleคอนโซลของ Bing เองBing 自己的后台 | Bing Webmaster Tools, imports from GoogleBing Webmaster Tools นำเข้าจาก Google ได้Bing 网站管理员工具,可从 Google 导入 |
🇬🇧 English
Next: a bigger site with Astro, classes, taught with a window, or the twelve week plan that starts with this.
🇹🇭 ไทย
ต่อไป: เว็บที่ใหญ่ขึ้นด้วย Astro, คลาส สอนด้วยหน้าต่าง หรือ แผนสิบสองสัปดาห์ ที่เริ่มจากตรงนี้
🇨🇳 中文
接下来:用 Astro 做更大的站、用一个窗口来讲类,或者从这里开始的十二周计划。