使用 Nginx 代理解決前端開(kāi)發(fā)的跨域問(wèn)題

Nginx(發(fā)音同“engine X”)是異步框架的網(wǎng)頁(yè)服務(wù)器店乐,也可以用作反向代理器一、負(fù)載平衡器和HTTP緩存。

本文會(huì)講述如何使用 Nginx 的代理功能幫助進(jìn)行前端頁(yè)面的開(kāi)發(fā)强窖。

Web 開(kāi)發(fā)通常使用的是前后端分離的開(kāi)發(fā)模式释树,即前端和后端分別進(jìn)行開(kāi)發(fā),前端通過(guò) Ajax 請(qǐng)求后端的接口乡小,將獲取數(shù)據(jù)將數(shù)據(jù)渲染到頁(yè)面上阔加。前端開(kāi)發(fā)會(huì)使用腳手架搭建前端開(kāi)發(fā)環(huán)境,其底層通常會(huì)啟動(dòng)一個(gè)本地服務(wù)器满钟,通常使用的是 nodejs 的 Express 框架胜榔。而后端則是提供接口,一般是放在線上的一個(gè)開(kāi)發(fā)用的域名下湃番。

這在開(kāi)發(fā)過(guò)程中會(huì)導(dǎo)致 跨域 問(wèn)題夭织,即在一個(gè)域名下的網(wǎng)頁(yè),是無(wú)法通過(guò) Ajax 請(qǐng)求另一個(gè)(不同源)域名下的接口 API 的吠撮。這是瀏覽器的同源策略尊惰,是瀏覽器的一個(gè)非常重要的安全策略。

解決這個(gè)問(wèn)題的其中一個(gè)方案是使用 代理。具體來(lái)說(shuō)弄屡,就是在本地啟動(dòng)一個(gè)服務(wù)器(如 localhost:4000)题禀,發(fā)送給該服務(wù)器的請(qǐng)求會(huì)根據(jù)請(qǐng)求路由(比如判斷 url 是否有前綴 /api)進(jìn)行轉(zhuǎn)發(fā),分別轉(zhuǎn)發(fā)到前端開(kāi)發(fā)的服務(wù)器(如 localhost:3000)琢岩,以及后端服務(wù)器(比如 dev.yoursite.com)投剥。這樣通過(guò)一個(gè)代理服務(wù)器,因?yàn)檎?qǐng)求的 api 都是同一個(gè)域名下的担孔,自然就不會(huì)造成跨域問(wèn)題江锨,從而導(dǎo)致請(qǐng)求失敗。

下面我們就來(lái)講解如何使用 Nginx 來(lái)實(shí)現(xiàn)反向代理糕篇。

簡(jiǎn)單認(rèn)識(shí) Nginx 配置文件

安裝好 Nginx 后啄育,我們需要確定下 Nginx 的默認(rèn)配置文件的位置。執(zhí)行命令 nginx -t拌消,該命令會(huì)檢測(cè) nginx 的默認(rèn)配置文件語(yǔ)法是否正確挑豌,并進(jìn)行測(cè)試,最后輸出結(jié)果墩崩。我們可以從輸出中得到默認(rèn)配置文件所在的位置氓英。

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

還有另外一種方法可以得到默認(rèn)配置文件的位置,那就是執(zhí)行 nginx -h鹦筹。該命令會(huì)輸出 nginx 的簡(jiǎn)易幫助文檔铝阐,其中的 -c filename 的配置項(xiàng)說(shuō)明也指出了默認(rèn)配置項(xiàng)的路徑。

 -c filename   : set configuration file (default: /etc/nginx/nginx.conf)

通過(guò)這個(gè)文檔铐拐,我們也可以知道徘键,使用 -c 配置項(xiàng)可以自定義配置文件。如果不指定文件遍蟋,使用默認(rèn)配置文件吹害。

下面我們來(lái)修改一下 Nginx 到這個(gè)默認(rèn)配置文件 nginx.config 來(lái)實(shí)現(xiàn)代理功能。

nginx.config 文件的 http 后面的代碼塊中虚青,應(yīng)該會(huì)有類(lèi)似下面這行的代碼:

include /etc/nginx/conf.d/*.conf;

這行代碼的作用是將 /etc/nginx/conf.d 目錄下的后綴為 .conf 的文件內(nèi)容嵌入到引入位置中它呀,作為配置的一部分執(zhí)行。

如果你是在 macOS 安裝的 Nginx挟憔,可能會(huì)有點(diǎn)不同钟些。我使用 brew 安裝的 Nginx 為為 include servers/*;,對(duì)應(yīng)嵌入的是 servers 目錄下的所有文件绊谭。

為什么要用到這種嵌入的語(yǔ)法呢政恍?因?yàn)檫@樣可以將不同項(xiàng)目需要用到的配置放到不同的配置文件里,好處是可以快速地找到對(duì)應(yīng)項(xiàng)目要修改的配置文件达传,不用擔(dān)心不小心修改了其他項(xiàng)目的配置篙耗。另外如果全部都直接在 nginx.conf 上修改迫筑,會(huì)讓這個(gè)文件變得臃腫,當(dāng)配置多的時(shí)候變得難以配置宗弯。這符合設(shè)計(jì)模式的 單一職責(zé)原則脯燃。

此外,你可能會(huì)奇怪 conf.d 目錄的命名為什么要加上 .d 蒙保?如果你使用 Linux 過(guò)一段時(shí)間辕棚,你會(huì)發(fā)現(xiàn)某些目錄或文件的末尾會(huì)加上一個(gè) d,比如 httpd邓厕、crond逝嚎、vsftpd 等。其實(shí)這是為了說(shuō)明這些文件都屬于是 daemon(服務(wù))详恼。這里的服務(wù)指的是系統(tǒng)的服務(wù)补君,主要分為系統(tǒng)本身需要的服務(wù),以及負(fù)責(zé)網(wǎng)絡(luò)的服務(wù)昧互。我們的 conf.d 屬于后者挽铁。

編寫(xiě) Nginx 配置文件

我們?cè)?conf.d 目錄下創(chuàng)建名為 demo.conf 的文件,寫(xiě)入以下內(nèi)容敞掘,然后啟動(dòng) Nginx叽掘。

server {
  listen 5000;
  server_name localhost;

  location / {
    proxy_pass http://localhost:3000;
  }
  location /api/ {
    proxy_pass http://localhost:4000;
  }
}

該配置啟用了 localhost:5000 的服務(wù)器,將 localhost:5000 下開(kāi)頭為 /api/ url 請(qǐng)求代理到了 localhost:4000(后端接口服務(wù)器)玖雁。其他請(qǐng)求則是代理到 localhost:3000(前端)够掠。下面我們具體解析一下配置文件里面內(nèi)容的作用。

listen 設(shè)置了服務(wù)器的端口號(hào)茄菊,server_name 則設(shè)置了主機(jī)名。

location

location 表示進(jìn)行路由的匹配赊堪,如果匹配則執(zhí)行對(duì)應(yīng)代碼塊里的操作面殖。location 可以使用 前綴匹配 以及 正則匹配(需要以 ~*~ 開(kāi)頭)。我們這里的配置使用的是前綴匹配哭廉。

這里有個(gè)點(diǎn)需要注意一下脊僚,Nginx 的路由匹配和一般的按順序匹配第一個(gè)的路由匹配方案(比如后端的 gin、前端的 vue-router 的路由匹配方案)不同遵绰,nginx 匹配路由的方式為:

  1. 首先進(jìn)行前綴匹配辽幌,遍歷所有的前綴匹配,從中選擇前綴匹配最長(zhǎng)的椿访;
  2. 然后會(huì)進(jìn)行正則匹配乌企,在所有正則匹配中,從前往后選擇第一個(gè)符合的成玫;
  3. 如果能找到匹配的正則匹配加酵,使用其對(duì)應(yīng)的配置拳喻;如果沒(méi)有,則使用之前找到的那個(gè)最長(zhǎng)的前綴匹配對(duì)應(yīng)的配置猪腕。

所以冗澈,當(dāng)請(qǐng)求為 localhost:5000/api/xx 時(shí), //api/ 都能夠前綴匹配陋葡。根據(jù)規(guī)則亚亲,雖然位置更靠前的 / 也符合前綴匹配,但 /api 更長(zhǎng)腐缤,所以最終匹配的是 /api捌归。

proxy_pass

確定好匹配的 location 后,我們?cè)倏纯?proxy_pass 又做了什么操作柴梆。proxy_pass 用于將請(qǐng)求路由映射到指定的協(xié)議和地址陨溅。本質(zhì)是將發(fā)送給 Nginx 的請(qǐng)求處理并發(fā)送到另一個(gè)服務(wù)器,然后將返回的數(shù)據(jù)作為 Nginx的返回?cái)?shù)據(jù)返回绍在。

  • proxy_pass 后如果使用的是 URI(端口后面至少有一個(gè) /)门扇,那么 Nginx 就會(huì) 替換 掉 location 匹配的那部分字符。
listen 5000;
server_name localhost;
location /name/ {
    proxy_pass http://127.0.0.1/remote/; 
}
# localhost:5000/name/fstar
# 會(huì)被映射請(qǐng)求為
# 127.0.0.1/remote/fstar

可以看到偿渡,/name/ 的部分在映射時(shí)被移除(或者說(shuō)是替換)了臼寄。

  • proxy_pass 后如果使用的是不是 URI(端口后沒(méi)有任何東西),Nginx 會(huì)將源請(qǐng)求完全映射到代理服務(wù)上:
listen 5000;
server_name localhost;
location /some/path/ {
    proxy_pass http://127.0.0.1;
}

# localhost:5000/some/path/x/y
# 會(huì)被映射請(qǐng)求為
# 127.0.0.1/some/path/x/y

這里的 /some/path 并沒(méi)有被移除溜宽。

我們的 demo.conf 文件的 proxy_pass 使用的不是 URI吉拳,所以是將路由完全映射到另一個(gè)服務(wù)。

思考題

請(qǐng)問(wèn)适揉,下面有兩段配置(區(qū)別是 proxy_pass 結(jié)尾是否有 /)留攒,如果請(qǐng)求 /kite/api/xx,分別會(huì)映射為什么嫉嘀?

location /kite/api/ {
    proxy_pass http://localhost:5000;
}
location /kite/api/ {
    proxy_pass http://localhost:5000/;
}

前面我們講 proxy_pass 的時(shí)候說(shuō)過(guò)炼邀,proxy_pass 后面如果不是 URI,會(huì)正常轉(zhuǎn)發(fā)剪侮;如果是 URI拭宁,就移除 location 匹配的前綴再進(jìn)行轉(zhuǎn)發(fā),體現(xiàn)的是替換路由的效果瓣俯。上面這兩個(gè)配置的區(qū)別就在于末尾的這個(gè) /杰标,有 / 是 URI,沒(méi)有的不是 URI彩匕,從而導(dǎo)致完全不一樣的結(jié)果腔剂,依次分別為:

http://localhost:5000/kite/api/xx
http://localhost:5000/xx

所以,在寫(xiě) Nginx 配置的時(shí)候驼仪,一定要注意端口后面的 / 是否有必要保留桶蝎。因?yàn)樗挠袩o(wú)會(huì)導(dǎo)致兩種截然不同的效果驻仅。

文章首發(fā)于我的 個(gè)人博客網(wǎng)站

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市登渣,隨后出現(xiàn)的幾起案子噪服,更是在濱河造成了極大的恐慌,老刑警劉巖胜茧,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粘优,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡呻顽,警方通過(guò)查閱死者的電腦和手機(jī)雹顺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)廊遍,“玉大人嬉愧,你說(shuō)我怎么就攤上這事『砬埃” “怎么了没酣?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)卵迂。 經(jīng)常有香客問(wèn)我裕便,道長(zhǎng),這世上最難降的妖魔是什么见咒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任偿衰,我火速辦了婚禮,結(jié)果婚禮上改览,老公的妹妹穿的比我還像新娘下翎。我一直安慰自己,他們只是感情好宝当,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布漏设。 她就那樣靜靜地躺著,像睡著了一般今妄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸳碧,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天盾鳞,我揣著相機(jī)與錄音,去河邊找鬼瞻离。 笑死腾仅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的套利。 我是一名探鬼主播推励,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鹤耍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了验辞?” 一聲冷哼從身側(cè)響起稿黄,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎跌造,沒(méi)想到半個(gè)月后杆怕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡壳贪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年陵珍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片违施。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡互纯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出磕蒲,到底是詐尸還是另有隱情留潦,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布亿卤,位于F島的核電站愤兵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏排吴。R本人自食惡果不足惜秆乳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钻哩。 院中可真熱鬧屹堰,春花似錦、人聲如沸街氢。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)珊肃。三九已至荣刑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伦乔,已是汗流浹背厉亏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烈和,地道東北人爱只。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像招刹,于是被迫代替她去往敵國(guó)和親恬试。 傳聞我的和親對(duì)象是個(gè)殘疾皇子窝趣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345