blob: cc6d8d8935f602e66280f5370f7c63fae20a4cf9 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="author" content="韩暮秋">
<meta name="subtitle" content="暮秋小屋">
<meta name="description" content="这里是暮秋小屋,思念和灵感的寄存处">
<meta name="keywords" content="韩暮秋,MuqiuHan,'Muqiu Han', 'muqiu han', muqiuhan">
<title>
Nginx 前后端同域分流 |
暮秋小屋
</title>
<link rel="icon" href="/favicon.ico">
<!-- stylesheets list from _config.yml -->
<link rel="stylesheet" href="/css/style.css">
<link rel="preload" href="/fonts/TensentypeCode.woff2" as="font" type="font/woff2" crossorigin>
<!-- scripts list from _config.yml -->
<script
src="/js/menu.js"></script>
<script
src="https://polyfill.alicdn.com/polyfill.js?features=es6"></script>
<script
id="MathJax-script"
async
src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/mathjax/3.2.0/es5/tex-mml-chtml.js"></script>
<meta name="generator" content="Hexo 6.3.0"></head>
<body>
<div class="wrapper">
<div class="header">
<div class="flex-container">
<div class="header-inner">
<div class="site-brand-container">
<a href="/">
暮秋小屋
</a>
</div>
<div id="menu-btn" class="menu-btn" onclick="toggleMenu()">
菜单
</div>
<nav class="site-nav">
<ul class="menu-list">
<li class="menu-item">
<a href="/">
主页
</a>
</li>
<li class="menu-item">
<a href="/categories/gallery/">
日记本
</a>
</li>
<li class="menu-item">
<a href="/tags/Medicine/">
泛医学
</a>
</li>
<li class="menu-item">
<a href="/tags/Technique/">
计算机
</a>
</li>
<li class="menu-item">
<a href="/tags/Life/">
生活
</a>
</li>
<li class="menu-item">
<a href="/archives">
全部
</a>
</li>
<li class="menu-item">
<a href="/about">
关于
</a>
</li>
<li class="menu-item">
<a href="/search">搜索</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="main">
<div class="flex-container">
<article id="post">
<div class="post-head">
<div class="post-info">
<div class="tag-list">
<span class="post-tag">
<a href="/tags/Technique/">
Technique
</a>
</span>
</div>
<div class="post-title">
Nginx 前后端同域分流
</div>
<span class="post-date">
Mar 16, 2026
</span>
</div>
<div class="post-img">
<div class="h-line-primary"></div>
</div>
</div>
<div class="post-content">
<p>今天我想在服务器上的 docker nginx 中配置:同一主域下,前端走 <code>/</code>,后端 API 走 <code>/api/*</code>,认证框架 Better Auth 走 <code>/api/auth/*</code>。后端还有自己的 <code>/auth/*</code> 业务接口。</p>
<p>例如 xxx.yyy.zzz 是前端,xxx.yyy.zzz/api 是后端,后端基于 NestJS 用了 better-auth 会自动挂 /auth 路径。</p>
<p>Nginx 跑在 Docker 容器里,配置和证书从宿主机挂载进去:</p>
<ul>
<li><code>/opt/nginx/conf.d</code> → <code>/etc/nginx/conf.d</code></li>
<li><code>/opt/nginx/ssl</code> → <code>/etc/nginx/ssl</code></li>
<li><code>/opt/nginx/nginx.conf</code> → <code>/etc/nginx/nginx.conf</code></li>
</ul>
<p>容器通过 <code>extra_hosts: host.docker.internal:host-gateway</code> 访问宿主机服务,所以前后端进程可以继续跑在宿主机上(比如 PM2 管理),Nginx 容器只做反代。</p>
<p>遇到的第一个坑:<code>/api/auth/*</code> 和 <code>/api/*</code> 不能用同一个 rewrite 规则。</p>
<p>如果这么写:</p>
<figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">location</span> /api/ {</span><br><span class="line"> <span class="attribute">proxy_pass</span> http://host.docker.internal:<backend>/;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Nginx 会把 <code>/api</code> 前缀裁掉。于是 <code>/api/auth/sign-in/email</code> 变成了 <code>/auth/sign-in/email</code>,但 better-auth 挂在 <code>/api/auth/*</code>,直接 404。</p>
<p>正确的做法是分开处理。更麻烦的是业务身份接口 <code>/auth/me</code> 也存在,如果后端业务接口是 <code>/auth/me</code>,而认证框架是 <code>/api/auth/*</code>,那 <code>/api/auth/me</code> 应该转发给业务而不是认证框架。</p>
<p>正确顺序:</p>
<figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">location</span> = /api/auth/me {</span><br><span class="line"> <span class="comment"># 精确匹配,业务身份接口</span></span><br><span class="line"> <span class="attribute">rewrite</span><span class="regexp"> ^/api(.*)$</span> <span class="variable">$1</span> <span class="literal">break</span>;</span><br><span class="line"> <span class="attribute">proxy_pass</span> http://host.docker.internal:<backend>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="section">location</span><span class="regexp"> ^~</span> /api/auth/ {</span><br><span class="line"> <span class="comment"># 认证框架路由,保留前缀</span></span><br><span class="line"> <span class="attribute">proxy_pass</span> http://host.docker.internal:<auth-port>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="section">location</span><span class="regexp"> ^~</span> /api/ {</span><br><span class="line"> <span class="comment"># 其余 API,裁掉 /api</span></span><br><span class="line"> <span class="attribute">rewrite</span><span class="regexp"> ^/api(.*)$</span> <span class="variable">$1</span> <span class="literal">break</span>;</span><br><span class="line"> <span class="attribute">proxy_pass</span> http://host.docker.internal:<backend>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="section">location</span> / {</span><br><span class="line"> <span class="comment"># 前端</span></span><br><span class="line"> <span class="attribute">proxy_pass</span> http://host.docker.internal:<frontend>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这里的顺序很重要。Nginx 匹配规则是:精确匹配 <code>=</code> 优先,然后是前缀匹配 <code>^~</code>,然后是正则匹配,最后是普通前缀匹配。<code>^~</code> 表示一旦匹配成功就不再检查正则。</p>
</div>
<script>
window.onload = detectors();
</script>
<div class="post-footer">
<div class="h-line-primary"></div>
<nav class="post-nav">
<div class="prev-item">
<div class="icon arrow-left"></div>
<div class="post-link">
<a href="/2026/03/21/%E5%B0%8F%E8%B0%88-NestJS-Zod/">Prev</a>
</div>
</div>
<div class="next-item">
<div class="icon arrow-right"></div>
<div class="post-link">
<a href="/2026/03/12/%E7%AE%80%E5%8D%95%E8%AF%B4%E8%AF%B4-FHIR-Transaction-Bundle/">Next</a>
</div>
</div>
</nav>
</div>
<div class="post-comment">
</div>
</article>
</div>
</div>
<div class="footer">
<div class="flex-container">
<div class="footer-text">
韩暮秋 |
希望路过的人可以添点柴火让这里暖和点
</div>
</div>
</div>
</div>
<script src="/js/mermaid-zoom.js"></script>
</body>
</html>
|