Nginx 实践:同一端口为多个域名配置独立的 SSL 证书

Nginx 支持在同一个监听端口上配置多个虚拟主机(虚拟服务器),并且可以为每个虚拟主机配置不同的域名和证书。通过使用 server 块来实现这一点,每个 server 块可以有自己的域名和 SSL 证书配置。 下面是一个示例配置,展示了如何在同一个监听端口(例如 443 端口)上配置两个不同的域名,并为它们配置不同的 SSL 证书: # 配置第一个域名 server { listen 443 ssl; server_name example1.com; ssl_certificate /path/to/example1.com.crt; ssl_certificate_key /path/to/example1.com.key; location / { root /var/www/example1; index index.html; } } # 配置第二个域名 server { listen 443 ssl; server_name example2.com; ssl_certificate /path/to/example2.com.crt; ssl_certificate_key /path/to/example2.com.key; location / { root /var/www/example2; index index.html; } } 在这个配置中: listen 443 ssl; 指定 Nginx 监听 443 端口,并启用 SSL。 server_name 指定虚拟主机的域名。 ssl_certificate 和 ssl_certificate_key 指定每个域名对应的 SSL 证书和私钥。 location 块内配置了每个域名对应的文档根目录。 这样,当用户访问 https://example1.com 时,Nginx 会使用 example1.com 的证书和配置;当用户访问 https://example2.com 时,Nginx 会使用 example2.com 的证书和配置。 ...

五月 26, 2024 · 1 分钟 · 127 字 · Byter ·  Linux ·  Nginx ·  SSL

Nginx网站代理典型配置

五月 26, 2024 · 0 分钟 · 0 字 · Byter ·  Nginx

Linux下使用maestral同步dropbox文件

前面写过一篇 《Obsidian通过Dropbox同步实现文章自动发布》 但是没有具体写Linux下怎么使用maestral的介绍。之前在使用的时候,为了实现只同步制定目录下的文件,经历过惨痛的教训,把dropbox里上传的内容整个删除了好几次,好在有备份重新上传,这里记录下来,希望大家能避坑。 maestral的GitHub地址:https://github.com/samschott/maestral Maestral: https://maestral.app/ 安装maestral mkdir maestral cd maestral/ python3 -m venv venv source venv/bin/activate python3 -m pip install --upgrade maestral 授权maestral访问dropbox maestral auth link # 多账号可以加 -c 选项 # maestral auth link -c home # 如果没有多账号的情况,最好是不加-c选项,要不然后面所有的命令都需要这个参数,太麻烦了 出现提示后,选择第二项,在控制台打印授权URL - Linking new account for 'test' config - Retrieving auth code from Dropbox ? How would you like to you link your account? > Open Dropbox website Print auth URL to console 将URL复制到浏览器,登录dropbox账号之后,将页面上生成的访问码粘贴到命令行,粘贴过程中出现打字效果,耐心等待即可 回车之后,提示如下信息表示授权成功了! ...

五月 25, 2024 · 2 分钟 · 241 字 · Byter ·  软件 ·  Maestral

在服务器建立SSH隧道实现数据的转发

SSH 隧道是一种安全可靠的数据传输方式,它可以将数据加密并通过 SSH 连接进行转发,从而在不安全的网络环境中实现安全的数据传输。本文将介绍如何在服务器上建立 SSH 隧道,实现数据的转发。 为什么要使用 SSH 隧道? 安全数据传输: SSH 隧道通过加密数据传输,保护数据在网络传输过程中的安全。 绕过网络限制: SSH 隧道可以绕过防火墙等网络限制,访问被屏蔽的网站或服务。 远程访问本地资源: SSH 隧道可以将本地资源转发到远程服务器,实现远程访问本地资源。 建立 SSH 隧道的步骤 安装 SSH 客户端和服务器: 确保服务器和客户端都安装了 SSH 客户端和服务器。 配置 SSH 连接: 在客户端机器上,使用 SSH 命令连接到服务器,并指定端口转发规则。 启动 SSH 隧道: 执行 SSH 命令,启动 SSH 隧道。 验证 SSH 隧道: 使用网络工具验证 SSH 隧道是否成功建立。 示例 flowchart LR ssh1<-->ssh2 subgraph B ssh2-->HTTP服务 end subgraph A curl-->ssh1 end 上图B服务有HTTP服务,但是A不能直接访问到B,但是B可以访问到A。 于是通过B连接到A建立一个远程端口1080转发到本地80端口。 ssh -NfR 1080:localhost:80 -p 1022 -i user.key user@server_ip -N:表示不执行远程命令,只建立隧道。 -f:表示将 SSH 连接放到后台运行。 -R 1080:localhost:80:将远程 1080 端口转发到本地的 80 端口。 -L 1080:localhost:80:将本地 1080 端口转发到服务器的 80 端口。 -i user.key: 采用证书登录是使用 user@server_ip:服务器用户名和 IP 地址。 之后就可以使用SSH隧道访问本地资源了 ...

五月 19, 2024 · 1 分钟 · 124 字 · Byter ·  SSH ·  Linux

如何优雅流畅的使用ChatGPT gpt-4o服务

获取OpenAI API KEY 由于OpenAI官网的是不支持中国大陆的信用卡支付,这个步骤是比较复杂的。 目前是通过注册OCBC的借记卡实现支付,正常使用一个月了。OCBC银行卡办理网上有不少教程,不过关于推荐码不是一定要填,你不填也是可以注册成功,填了推荐码入金1000SGD之后推荐的人有奖励,个人返现不受推荐码限制。注册过程到拿到实体卡,到完全激活响应2周时间,入金通过工行转账2个小时就到账了。之后就可以付费了,选择美国免税区不用缴税。(题外话,原来办理OCBC是想着注册甲骨文云,实践下来不行) 另外还有一种简单方式 ,用中转服务,有一些汇率的手续费。 地址:https://api.oaipro.com/ 充值就可以用,用这个中转,不需要配置nginx,直接跳到one api配置。 代理OpenAI请求 Nginx代理 通过Nginx是把代理放到个人小机上面,这样要求你的小鸡能够正常访问OpenAI。 这种形式的好处就是以固定IP的形式访问服务。 在服务器运行测试命令,替换 OPENAI_API_KEY 为你的API key,有回复可以进行下一步 curl https://api.openai.com/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $OPENAI_API_KEY" \ -d '{ "model": "gpt-3.5-turbo-0125", "response_format": { "type": "json_object" }, "messages": [ { "role": "system", "content": "You are a helpful assistant designed to output JSON." }, { "role": "user", "content": "Who won the world series in 2020?" } ] }' Nginx配置server部分如下: ...

五月 18, 2024 · 3 分钟 · 579 字 · Byter ·  AI ·  OpenAI

2024-04 跑步日记

2024-04 运动次数: 7 运动距离: 51.64 km 运动时长: 5:14:57 平均距离: 7.38 km 平均心率: 149 bpm 平均配速: 6:05 / km 2024-04-08 时间: 2024-04-08 06:53:35 距离: 5.70 km 时长: 36:21 配速: 6:22 / km 心率: 144 bpm ...

四月 30, 2024 · 1 分钟 · 130 字 · Jogger ·  跑步

如何优雅流畅的使用Google Gemini 1.5 pro服务

获取Gemini API KEY 准备3-4个谷歌账号,因为5月2日之后谷歌就开始收费,Gemini 1.5免费1分钟2次请求,一天50次,多个账号均衡一下请求,满足日常使用。 可以按照大佬分享方法注册账号 无需手机号认证注册谷歌邮箱 进入谷歌获取Get API key 现在不用申请直接可以使用的1.5模型 代理谷歌请求 Cloudflare 代理 可以参考zhile的 我们也要用Gemini Pro Nginx代理 原理和上面一致,通过Nginx是把代理放到个人小机上面,这样要求你的小鸡能够正常访问谷歌。 这种形式的好处就是以固定IP的形式访问谷歌大模型服务。 在服务器运行测试命令,替换 YOUR_API_KEY 为你的API key,有回复可以进行下一步 curl \ -H 'Content-Type: application/json' \ -d '{"contents":[{"parts":[{"text":"Write a story about a magic backpack"}]}]}' \ -X POST 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=YOUR_API_KEY' Nginx配置server部分如下: server { listen 443 ssl; server_name _; root /xxx/html/web; # ssl on; ssl_certificate /xxx/xxx/ca.pem; ssl_certificate_key /xxx/xxx/private.key; allow 103.21.244.0/22; allow 103.22.200.0/22; allow 103.31.4.0/22; allow 141.101.64.0/18; allow 108.162.192.0/18; allow 190.93.240.0/20; allow 188.114.96.0/20; allow 197.234.240.0/22; allow 198.41.128.0/17; allow 162.158.0.0/15; allow 104.16.0.0/13; allow 104.24.0.0/14; allow 172.64.0.0/13; allow 131.0.72.0/22; deny all; location ^~ /Safari1261/ { # Google Gemini API 服务代理 proxy_ssl_server_name on; #proxy_pass https://api.openai.com; proxy_pass https://generativelanguage.googleapis.com/; proxy_buffering off; client_max_body_size 300m; } } 配置说明 ...

四月 14, 2024 · 4 分钟 · 684 字 · Byter ·  AI ·  Gemini

Obsidian通过Dropbox同步实现文章自动发布

graph LR 网易云笔记-->Evernote; Evernote-->为知笔记; 为知笔记-->Evernote; Evernote-->Joplin; Joplin-->Obsidian; 现在用的Obsidian是Markdown支持最好的,可以说写文档是一种享受了,但是要用官方的同步还是付费,于是就找有没有同步的方案,没有采用第三方插件Remotely Save或者是Remotely Sync,而是采用Dropbox直接同步文件形式,个人使用,没有太多的协同编写的场景。 graph TD B("fab:fa-dropbox Dropbox") B<-->C["fa:fa-laptop Obsidian maestral"] B<-->D["fa:fa-mobile-screen-button Obsidian Dropsync"] B<-->E["fab:fa-linux Hugo maestral"] PC端用maestral先同步dropbox文件,然后Obsidian直接在dropbox的同步目录创建仓库,之后编辑的内容就会实时同步了,可以Show Recent Change 功能查看已经同步的变更文件,有冲突的文件会有提示。 移动端使用Dropsync同步文件,然后Obsidian打开仓库,在PC安装的插件和设置都可以在移动端使用。需要注意的是,因为Dropsync 不是采用文件变化通知机制同步,而是定时同步机制,因此需要在手机端打开Obsidian之前,需要手动同步一下。另外就是最好PC端、移动端不要同时设置或者打开关闭笔记,会有同步文件冲突。有冲突的情况下,可以进入仓库查看笔记或者.obsidian目录下是否存在appearance (conflict 2024-04-15-08-35-19).json 类似的文件。 可以在Dropsync软件的同步历史查看同步日志信息。 服务器端主要用maestral在命令行下运行同步Dropbox文件,然后通过脚本把对应笔记md文件拷贝到 Hugo 目录下,编译发布成个人站点文章。服务器没有设置成一有文件变更就同步更新到Hugo,感觉有些频繁,我是设置了脚本的定时任务,每天晚上编译发布,确实想实时发布的,就到服务器上手动执行一下脚本。 整个流程下来对于使用基本是无感的,同步速度也很快,基本在PC端编辑完之后,你登录上服务器就可以看到同步好的文件。 用到的软件: Obsidian: https://obsidian.md/ Maestral: https://maestral.app/ Dropsync: https://metactrl.com/#our-apps

Hugo代码块固定高度设置

Hugo个人博客搭建过程参考Hugo个人博客搭建 新增code的css mkdir -p assets/css/extended vim assets/css/extended/code.css code.css内容如下: .post-content pre code { max-height: 40em; /* 根据需要调整高度 */ overflow: auto; } 设置目录到左侧是参考3rd’s Blog的博客 感谢

Hugo文章TOC固定在左侧显示

Hugo个人博客搭建过程参考Hugo个人博客搭建 在layouts/partials目录下创建toc.html vim layouts/partials/toc.html toc.html内容为: {{- $headers := findRE "<h[1-6].*?>(.|\n])+?</h[1-6]>" .Content -}} {{- $has_headers := ge (len $headers) 1 -}} {{- if $has_headers -}} <aside id="toc-container" class="toc-container wide"> <div class="toc"> <details {{if (.Param "TocOpen") }} open{{ end }}> <summary accesskey="c" title="(Alt + C)"> <span class="details">{{- i18n "toc" | default "Table of Contents" }}</span> </summary> <div class="inner"> {{- $largest := 6 -}} {{- range $headers -}} {{- $headerLevel := index (findRE "[1-6]" . 1) 0 -}} {{- $headerLevel := len (seq $headerLevel) -}} {{- if lt $headerLevel $largest -}} {{- $largest = $headerLevel -}} {{- end -}} {{- end -}} {{- $firstHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers 0) 1) 0)) -}} {{- $.Scratch.Set "bareul" slice -}} <ul> {{- range seq (sub $firstHeaderLevel $largest) -}} <ul> {{- $.Scratch.Add "bareul" (sub (add $largest .) 1) -}} {{- end -}} {{- range $i, $header := $headers -}} {{- $headerLevel := index (findRE "[1-6]" . 1) 0 -}} {{- $headerLevel := len (seq $headerLevel) -}} {{/* get id="xyz" */}} {{- $id := index (findRE "(id=\"(.*?)\")" $header 9) 0 }} {{- /* strip id="" to leave xyz, no way to get regex capturing groups in hugo */ -}} {{- $cleanedID := replace (replace $id "id=\"" "") "\"" "" }} {{- $header := replaceRE "<h[1-6].*?>((.|\n])+?)</h[1-6]>" "$1" $header -}} {{- if ne $i 0 -}} {{- $prevHeaderLevel := index (findRE "[1-6]" (index $headers (sub $i 1)) 1) 0 -}} {{- $prevHeaderLevel := len (seq $prevHeaderLevel) -}} {{- if gt $headerLevel $prevHeaderLevel -}} {{- range seq $prevHeaderLevel (sub $headerLevel 1) -}} <ul> {{/* the first should not be recorded */}} {{- if ne $prevHeaderLevel . -}} {{- $.Scratch.Add "bareul" . -}} {{- end -}} {{- end -}} {{- else -}} </li> {{- if lt $headerLevel $prevHeaderLevel -}} {{- range seq (sub $prevHeaderLevel 1) -1 $headerLevel -}} {{- if in ($.Scratch.Get "bareul") . -}} </ul> {{/* manually do pop item */}} {{- $tmp := $.Scratch.Get "bareul" -}} {{- $.Scratch.Delete "bareul" -}} {{- $.Scratch.Set "bareul" slice}} {{- range seq (sub (len $tmp) 1) -}} {{- $.Scratch.Add "bareul" (index $tmp (sub . 1)) -}} {{- end -}} {{- else -}} </ul> </li> {{- end -}} {{- end -}} {{- end -}} {{- end }} <li> <a href="#{{- $cleanedID -}}" aria-label="{{- $header | plainify -}}">{{- $header | safeHTML -}}</a> {{- else }} <li> <a href="#{{- $cleanedID -}}" aria-label="{{- $header | plainify -}}">{{- $header | safeHTML -}}</a> {{- end -}} {{- end -}} <!-- {{- $firstHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers 0) 1) 0)) -}} --> {{- $firstHeaderLevel := $largest }} {{- $lastHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers (sub (len $headers) 1)) 1) 0)) }} </li> {{- range seq (sub $lastHeaderLevel $firstHeaderLevel) -}} {{- if in ($.Scratch.Get "bareul") (add . $firstHeaderLevel) }} </ul> {{- else }} </ul> </li> {{- end -}} {{- end }} </ul> </div> </details> </div> </aside> <script> let activeElement; let elements; window.addEventListener('DOMContentLoaded', function (event) { checkTocPosition(); elements = document.querySelectorAll('h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]'); // Make the first header active activeElement = elements[0]; const id = encodeURI(activeElement.getAttribute('id')).toLowerCase(); document.querySelector(`.inner ul li a[href="#${id}"]`).classList.add('active'); }, false); window.addEventListener('resize', function(event) { checkTocPosition(); }, false); window.addEventListener('scroll', () => { // Check if there is an object in the top half of the screen or keep the last item active activeElement = Array.from(elements).find((element) => { if ((getOffsetTop(element) - window.pageYOffset) > 0 && (getOffsetTop(element) - window.pageYOffset) < window.innerHeight/2) { return element; } }) || activeElement elements.forEach(element => { const id = encodeURI(element.getAttribute('id')).toLowerCase(); if (element === activeElement){ document.querySelector(`.inner ul li a[href="#${id}"]`).classList.add('active'); } else { document.querySelector(`.inner ul li a[href="#${id}"]`).classList.remove('active'); } }) }, false); const main = parseInt(getComputedStyle(document.body).getPropertyValue('--article-width'), 10); const toc = parseInt(getComputedStyle(document.body).getPropertyValue('--toc-width'), 10); const gap = parseInt(getComputedStyle(document.body).getPropertyValue('--gap'), 10); function checkTocPosition() { const width = document.body.scrollWidth; if (width - main - (toc * 2) - (gap * 4) > 0) { document.getElementById("toc-container").classList.add("wide"); } else { document.getElementById("toc-container").classList.remove("wide"); } } function getOffsetTop(element) { if (!element.getClientRects().length) { return 0; } let rect = element.getBoundingClientRect(); let win = element.ownerDocument.defaultView; return rect.top + win.pageYOffset; } </script> {{- end }} 修改toc的css ...

四月 14, 2024 · 4 分钟 · 838 字 · Byter ·  Hugo ·  TOC