2026/02/26(木)nginxからApache2へセキュアにリバプロしたときに真のクライアントIPを取得できるようにする
投稿日:
nginxからApache2のバーチャルホストにいい感じにリバプロする方法の続きで、nginxからApache2にリバースプロキシされた時にクライアントのIPを拾う設定の方法について。
確認環境
| Env | Ver |
|---|---|
| OS | Ubuntu 24.04.3 LTS |
| nginx | 1.26.1 |
| Apache2 | 2.4.58 |
やりたいこと
Apache2で動いているCGIのREMOTE_ADDRからクライアントIPを取れるようにする。
nginxからApache2のバーチャルホストにいい感じにリバプロする方法ではApache2側でREMOTE_ADDRを取得しようとするとnginxのIPになってしまうため、これを回避する方法。
前提条件
- nginxからApache2へのリバースプロキシをする時に、真のクライアントIPを
X-Real-IPヘッダーに入れていること。proxy_set_header X-Real-IP $remote_addr; - nginxとApacheが動いているマシンが同じ
- IPv6を使っている
やること
# remoteipモジュールの有効化
sudo a2enmod remoteip
# ::1からのHTTPリクエストを信頼しX-Real-IPをREMOTE_ADDRとして解釈する
cat <<'EOF' | sudo tee /etc/apache2/conf-available/remoteip.conf
RemoteIPHeader X-Real-IP
RemoteIPInternalProxy 127.0.0.1
RemoteIPInternalProxy ::1
EOF
# 設定の有効化
sudo a2enconf remoteip
# 設定の反映
sudo systemctl restart apache2
意味合いの解説
sudo a2enmod remoteip
remoteipモジュールの有効化をしている。
逆はa2dismodで無効化出来るっぽい。
RemoteIPHeader <header-field>
ここで指定したHTTPヘッダーをクライアントのIPアドレス(useragent IP address)として扱うようにする。
例えばRemoteIPHeader X-Forwarded-ForとすればX-Forwarded-ForをクライアントIPとして扱える。
この設定だけだと問答無用で信頼してしまうため、本来と異なる経路で攻撃を受けた時に偽装されたヘッダをクライアントIPとして解釈するリスクがある。そのため、次項の設定も行う。
参考:mod_remoteip - Apache HTTP Server Version 2.4
RemoteIPInternalProxy <proxy-ip|proxy-ip/subnet|hostname ...>
RemoteIPHeader単品では前段のnginxを無視してApache2へ直にアクセスされたときに偽のX-Real-IPなどが来ていると、偽のIPが取れてしまうので、これを回避する仕組み。
RemoteIPInternalProxy ::1のように指定することで、そのIPから来たRemoteIPHeaderを信頼する。異なるIPからリクエストが来た場合は、RemoteIPHeaderを無視して、そのクライアントのIPが設定される。
RemoteIPTrustedProxyでも同じことができるのだが、違いはよくわかっていない。一応内部用と外部用で分かれているらしいが、RemoteIPTrustedProxy ::1でも期待通り機能したので謎。
公式ドキュメントではIPv4アドレスが指定されているので、恐らくIPv4でも行けると思うが確認していない。
参考:mod_remoteip - Apache HTTP Server Version 2.4
sudo a2enconf remoteip
作成した設定の有効化を行う。これを行っていない場合、設定は反映されない。
やっていることは/etc/apache2/conf-available/にある設定ファイルのシンボリックリンクを/etc/apache2/conf-enabled/に張っているだけだと思われる。
標準では/etc/apache2/apache2.confでIncludeOptional conf-enabled/*.confを指定しているため、これで設定が読み込まれるようになる。
逆はa2disconfで無効化できる。
あとがき
ここまで書いたところで/etc/apache2/sites-available/と/etc/apache2/sites-enabled/の違いに気が付いた。これも多分有効無効を切り替えるもので、sites-enabled側に設定を直書きするのは誤っているのだろう、きっと。
2026/02/22(日)OpenWrtからValue-Domainに複数サブドメインのDDNSを行うツールを作った
Value-DomainのダイナミックDNSエンドポイントは60秒以内に叩くとエラーが返ってくるので、これを回避するためのDDNSの仕組みを作った話。
やったこと
まず、Value-DomainのDNS APIに対し、既存のaレコードをバルクで差し替えるためのツールとしてvd-ddns_v4.plを作った。
そしてhotplug.dにPPPoEインターフェースのIPが変わったときに、このスクリプトを蹴る処理を書いた。
この記事の前提構成
OpenWrtにIPv4用のPPPoEインターフェースがある。
やったこと
- 今回利用するのに必要なPerl周りをセットアップする。ストレージの空きが3.2MBほど必要
- value-domain-dns-utilを
/root配下とか適当な場所に放り込むopkg install openssh-sftp-serverでSFTPを導入しておくとファイル移動に便利
vi /etc/hotplug.d/iface/40-pppoeとかして、OpenWrtのインターフェースが変化したときのHookを作る#!/bin/sh # デバイスが存在しなければ終了 [ -n "$DEVICE" ] || exit 0 # リンクアップでなければ終了 [ "$ACTION" = ifup ] || exit 0 # インターフェース名がPPPoEのものでなければ終了 [ "$INTERFACE" = wanppp ] || exit 0 # pppoe-wanpppのIPv4アドレスを取得 pppoeaddr=$(ip -4 addr show pppoe-wanppp | head -2 | tail -1 | awk '{print $2}') # ログに吐く logger -t "DDNS - PPPoE IP" $pppoeaddr # DDNSもどきを叩く /root/vd-dns-util/vd-ddns_v4.pl 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' example.com $pppoeaddr hoge fuga piyo # 結果をログに吐きたいが何故か動いていない if [ $? -eq 0 ]; then logger -t "DDNS - UPDATE SUCCEED" else logger -t "DDNS - UPDATE FAIL" fiPPPoEインターフェスをRestart
- Value-Domainのコンパネで指定したドメインのaレコードが更新されていることを確認
備考
一般的にDNSレコードの浸透時間は更新前に浸透していたTTLに依存し、Value DomainのAPI経由で普段から操作している場合は120秒以下にならないため、最大120秒のダウンタイムが発生するが、理論上は公式のDDNS機能と大差ないはずと思われる。
aレコードのみの対応にしているのは私の環境だとIPv6は変動しないが、v4はそれなりの頻度で変わるためだ。
運用しているMastodonのv4が変わったのに気づかないまま疎通できないインスタンスが出てくることがしばしばあり、手動で対応するのが手間なのと、毎回一日くらい気づくのに遅れるので今回自動化に踏み切った。前々からやり勝ったのだが、中々腰が重く進んでいなかった。
そもそもこの手のものは監視システムで検知できて然るべきなので、おいおい監視システムの構築もしていきたいところだ。
2026/02/22(日)OpenWrtにPerlを入れてHTTPSやJSON、自作ライブラリを扱えるようにする
投稿日:
確認環境
OpenWrt 24.10.0
やり方
opkg install perl
opkg install perlbase-essential
opkg install perlbase-json-pp
opkg install perlbase-scalar
opkg install perlbase-xsloader
opkg install perlbase-dynaloader
opkg install perlbase-list
opkg install perlbase-bytes
opkg install perlbase-findbin
opkg install perlbase-http-tiny
# OpenWrtにはPerlのSSLモジュールがないので親切な人が作ってくれてるものを使わせていただく
# https://jw2013.github.io/openwrt-packages/
opkg install libustream-mbedtls
wget https://jw2013.github.io/openwrt-packages/public.key
opkg-key add public.key
echo 'src/gz pkgs-by-jw2013 https://jw2013.github.io/openwrt-packages/24.10/x86_64' >> /etc/opkg/customfeeds.conf
opkg update
opkg install perl-net-ssleay
opkg install perl-io-socket-ssl
rm public.key
増加する容量
キビバイトやメビバイトのバイト変換やトータルビバイトはLLMに換算させた欠化を書いているので誤りが混ざっている可能性がある。大まかには合っていると思うが…。
| pkg | size | byte |
|---|---|---|
| perl | 1.27 MiB | 1,331,692 |
| perl-io-socket-ssl | 124.00 KiB | 126,976 |
| perl-net-ssleay | 153.61 KiB | 157,297 |
| perlbase-autoloader | 3.04 KiB | 3,113 |
| perlbase-b | 106.07 KiB | 108,616 |
| perlbase-base | 4.27 KiB | 4,372 |
| perlbase-bytes | 1.23 KiB | 1,260 |
| perlbase-class | 3.02 KiB | 3,092 |
| perlbase-config | 18.55 KiB | 18,995 |
| perlbase-cwd | 11.31 KiB | 11,581 |
| perlbase-dynaloader | 4.50 KiB | 4,608 |
| perlbase-errno | 3.22 KiB | 3,297 |
| perlbase-essential | 23.79 KiB | 24,361 |
| perlbase-fcntl | 7.36 KiB | 7,537 |
| perlbase-feature | 2.68 KiB | 2,744 |
| perlbase-file | 72.30 KiB | 74,035 |
| perlbase-filehandle | 1.81 KiB | 1,853 |
| perlbase-findbin | 1.98 KiB | 2,028 |
| perlbase-http-tiny | 12.98 KiB | 13,292 |
| perlbase-i18n | 21.21 KiB | 21,719 |
| perlbase-integer | 1.12 KiB | 1,147 |
| perlbase-io | 67.71 KiB | 69,335 |
| perlbase-json-pp | 11.96 KiB | 12,247 |
| perlbase-list | 23.14 KiB | 23,695 |
| perlbase-locale | 10.98 KiB | 11,244 |
| perlbase-mime | 6.50 KiB | 6,656 |
| perlbase-params | 4.02 KiB | 4,116 |
| perlbase-posix | 40.45 KiB | 41,421 |
| perlbase-re | 285.55 KiB | 292,403 |
| perlbase-scalar | 1.60 KiB | 1,638 |
| perlbase-selectsaver | 1.27 KiB | 1,300 |
| perlbase-socket | 19.49 KiB | 19,958 |
| perlbase-symbol | 1.94 KiB | 1,987 |
| perlbase-tie | 18.40 KiB | 18,842 |
| perlbase-unicore | 779.16 KiB | 797,860 |
| perlbase-utf8 | 1.24 KiB | 1,270 |
| perlbase-xsloader | 2.54 KiB | 2,601 |
| total | 3.08 MiB | 3,230,188 |
2026/02/13(金)Matomoで直帰ユーザーの滞在時間を取得できるようにする
投稿日:
Matomoは標準では直帰ユーザーの滞在時間が取れないが、それをできるようにする方法。
確認環境
Matomo Version 5.7.1
やり方
トラッキングコードに_paq.push(['enableHeartBeatTimer', 10]);を追加するだけ。
公式ドキュメントを読む限り、これをすることで10秒以上閲覧した場合に滞在時間が取れるようになると思われる。なお、HeartBeatTimerという名前だが、眺めてみた感じ定期的にAPIリクエストを送ったりはしていないようだった。
取り敢えず10秒以下は0と同じでいいと思ったので10にしておくことにした。ビジット継続時間単位のビジットでも11秒以上の敷居があるので、キリもいい。11秒にしたほうがよりノイズが減り、正確かもしれないが、個人的には運用上10秒が取れても害はないので、細かいことは気にしないでおく。
なお、かつては名前の通り指定時間にポーリングしていたようだ。また5秒未満を指定しても機能しないらしい。
コードの追加例
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
var u="https://analytics.example.com/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
+ _paq.push(['enableHeartBeatTimer', 10]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName ('script') [0];
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
</script>
<!-- End Matomo Code -->
あとがき
公式ドキュメントの説明が薄いので、具体的にどう処理されているかはソースコードを読まないとわからないが、取り敢えず直帰ユーザーの滞在時間が見れるようになったので良かった。
一律で0sだと、内容を見ているユーザーと見てないユーザーが区別できないので、ここが分かるのは便利だ。
因みにGoogle Analyticsでも直帰は0秒扱いらしいが、そもそも最近この項目自体が削除されて、別概念に置き換わったらしい。
こういったことを踏まえると、やはり私のような個人サイト運営者にとってはMatomoは非常に便利なツールだ。
2026/02/12(木)MatomoでビジターIDごとのビジット一覧が見れるプラグインを作った
常連さんのアクセスを観察するのに標準のMatomoだとダッシュボード→利用状況→訪問数別のビジットである程度は見れるのだが、ビジット回数がベースでビジターがごちゃ混ぜになっていて辛い問題があったので、ビジターとビジットIDを一覧で見れるやつを作った。
コードはMatomo_Plugin_VisitorIdReportに置いてるので、もし使いたい人がいたら持っていって欲しい。
画面イメージはこんな感じ。
苦労したこと
公式の開発リファレンスが更新されてないのか、そのままでは上手くいかなかったり、あまり親切ではなく、どちらかと言えば有料プラグイン買ってねみたいな雰囲気だったので情報がなさ過ぎて苦労した。
ただ昨今はLLMという文明の利器があるので、大まかにはClaude Opus 4.6に書かせて、バグっていたらアタリを見つけて修正させて、どうにもならないところは手で直した。
推移グラフは実装方法が不明だったのでいったん放置しているが、ひとまずビジターID単位のビジット数が見れて、セグメント化ボタンからビジットの履歴を終えるところまでは作れたので満足だ。



