diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
new file mode 100644
index 00000000..281099e0
--- /dev/null
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/Nodeinfo2_0.kt
@@ -0,0 +1,16 @@
+package dev.usbharu.hideout.activitypub.application.nodeinfo
+
+@Suppress("ClassName")
+data class Nodeinfo2_0(
+    val version: String = "2,0",
+    val software: Map<String, String>,
+    val protocol: List<String>,
+    val usage: NodeinfoUsage,
+    val openRegistration: Boolean,
+    val metadata: Map<String, Any>,
+)
+
+data class NodeinfoUsage(
+    val users: Map<String, Long>,
+    val localPosts: Long,
+)
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
new file mode 100644
index 00000000..e057084f
--- /dev/null
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoApplicationService.kt
@@ -0,0 +1,52 @@
+package dev.usbharu.hideout.activitypub.application.nodeinfo
+
+import dev.usbharu.hideout.core.application.shared.AbstractApplicationService
+import dev.usbharu.hideout.core.application.shared.Transaction
+import dev.usbharu.hideout.core.config.ApplicationConfig
+import dev.usbharu.hideout.core.domain.model.support.principal.Principal
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.info.BuildProperties
+import org.springframework.stereotype.Service
+
+@Service/*
+ * 今後ユーザーエージェントに応じてレスポンスを変更する可能性があるためApplicationServiceとして作成する commandは現時点ではUnitだが今後変化する可能性あり
+ */
+class NodeinfoApplicationService(
+    @Autowired(required = false) private val buildInfo: BuildProperties?,
+    private val applicationConfig: ApplicationConfig,
+    transaction: Transaction,
+) : AbstractApplicationService<NodeinfoRequest, Nodeinfo2_0>(
+    transaction,
+    logger
+) {
+    override suspend fun internalExecute(command: NodeinfoRequest, principal: Principal): Nodeinfo2_0 {
+        return when (command.version) {
+            "2.0", "2.1" -> Nodeinfo2_0(
+                version = command.version,
+                software = mapOf(
+                    "name" to "hideout",
+                    "version" to (buildInfo?.version ?: "UNKNOWN")
+                ),
+                protocol = listOf("activitypub"),
+                NodeinfoUsage(
+                    users = mapOf(
+                        "total" to 0,
+                        "activeMonth" to 0,
+                        "activeHalfyear" to 0
+                    ),
+                    localPosts = 0
+                ),
+                openRegistration = applicationConfig.private.not(),
+                metadata = mapOf()
+            )
+
+            else -> throw IllegalArgumentException("Invalid command version ${command.version}")
+        }
+    }
+
+    companion object {
+        private val logger: Logger = LoggerFactory.getLogger(NodeinfoApplicationService::class.java)
+    }
+}
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt
new file mode 100644
index 00000000..7176f3d8
--- /dev/null
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/application/nodeinfo/NodeinfoRequest.kt
@@ -0,0 +1,3 @@
+package dev.usbharu.hideout.activitypub.application.nodeinfo
+
+data class NodeinfoRequest(val version: String)
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/config/ActivityPubSecurityConfig.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/config/ActivityPubSecurityConfig.kt
index b445a086..9dfbbe4c 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/config/ActivityPubSecurityConfig.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/config/ActivityPubSecurityConfig.kt
@@ -24,6 +24,7 @@ class ActivityPubSecurityConfig {
             )
             authorizeHttpRequests {
                 authorize(GET, "/.well-known/**", permitAll)
+                authorize(GET, "/nodeinfo/**", permitAll)
                 authorize(GET, "/error", permitAll)
                 authorize(POST, "/inbox", permitAll)
                 authorize(POST, "/users/{username}/inbox", permitAll)
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/HostmetaController.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/HostmetaController.kt
index 7b11c9cb..81641d1a 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/HostmetaController.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/HostmetaController.kt
@@ -19,12 +19,8 @@ class HostmetaController(private val linkList: List<Link> = emptyList()) {
 
     @Order(2)
     @GetMapping("/host-meta", produces = ["application/json"])
-    fun hostmetaJson(): XRD {
-        return XRD(linkList)
-    }
+    fun hostmetaJson(): XRD = XRD(linkList)
 
     @GetMapping("/host-meta.json", produces = ["application/json"])
-    fun hostmetaJson2(): XRD {
-        return XRD(linkList)
-    }
+    fun hostmetaJson2(): XRD = XRD(linkList)
 }
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
new file mode 100644
index 00000000..4fc95339
--- /dev/null
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/NodeinfoController.kt
@@ -0,0 +1,35 @@
+package dev.usbharu.hideout.activitypub.interfaces.wellknown
+
+import dev.usbharu.hideout.activitypub.application.nodeinfo.Nodeinfo2_0
+import dev.usbharu.hideout.activitypub.application.nodeinfo.NodeinfoApplicationService
+import dev.usbharu.hideout.activitypub.application.nodeinfo.NodeinfoRequest
+import dev.usbharu.hideout.core.config.ApplicationConfig
+import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
+import org.springframework.web.bind.annotation.GetMapping
+import org.springframework.web.bind.annotation.RestController
+
+@RestController
+class NodeinfoController(
+    private val applicationConfig: ApplicationConfig,
+    private val nodeinfoApplicationService: NodeinfoApplicationService,
+) {
+    @GetMapping("/.well-known/nodeinfo", produces = ["application/json"])
+    fun nodeinfo(): XRD = XRD(
+        listOf(
+            Link(
+                "http://nodeinfo.diaspora.software/ns/schema/2.1",
+                href = applicationConfig.url.resolve("/nodeinfo/2.1").toString()
+            ),
+            Link(
+                "http://nodeinfo.diaspora.software/ns/schema/2.0",
+                href = applicationConfig.url.resolve("/nodeinfo/2.0").toString()
+            )
+        )
+    )
+
+    @GetMapping("/nodeinfo/2.0", produces = ["application/json"])
+    suspend fun nodeinfo2_0(): Nodeinfo2_0 = nodeinfoApplicationService.execute(NodeinfoRequest("2.0"), Anonymous)
+
+    @GetMapping("/nodeinfo/2.1", produces = ["application/json"])
+    suspend fun nodeinfo2_1(): Nodeinfo2_0 = nodeinfoApplicationService.execute(NodeinfoRequest("2.1"), Anonymous)
+}
diff --git a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/XRD.kt b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/XRD.kt
index e6617c3f..02a9792d 100644
--- a/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/XRD.kt
+++ b/hideout/hideout-activitypub/src/main/kotlin/dev/usbharu/hideout/activitypub/interfaces/wellknown/XRD.kt
@@ -24,8 +24,9 @@ data class XRD(
 data class Link(
     @JacksonXmlProperty(localName = "rel", isAttribute = true) val rel: String,
     @JsonInclude(JsonInclude.Include.NON_NULL)
-    @JacksonXmlProperty(localName = "template", isAttribute = true) val template: String?,
-    @JacksonXmlProperty(localName = "type", isAttribute = true) val type: String,
+    @JacksonXmlProperty(localName = "template", isAttribute = true) val template: String? = null,
     @JsonInclude(JsonInclude.Include.NON_NULL)
-    @JacksonXmlProperty(localName = "href", isAttribute = true) val href: String?,
+    @JacksonXmlProperty(localName = "type", isAttribute = true) val type: String? = null,
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @JacksonXmlProperty(localName = "href", isAttribute = true) val href: String? = null,
 )
diff --git a/hideout/hideout-activitypub/src/test/kotlin/dev/usbharu/hideout/activitypub/application/webfinger/NodeinfoApplicationServiceTest.kt b/hideout/hideout-activitypub/src/test/kotlin/dev/usbharu/hideout/activitypub/application/webfinger/NodeinfoApplicationServiceTest.kt
new file mode 100644
index 00000000..8888de4f
--- /dev/null
+++ b/hideout/hideout-activitypub/src/test/kotlin/dev/usbharu/hideout/activitypub/application/webfinger/NodeinfoApplicationServiceTest.kt
@@ -0,0 +1,52 @@
+package dev.usbharu.hideout.activitypub.application.webfinger
+
+
+import dev.usbharu.hideout.activitypub.application.nodeinfo.NodeinfoApplicationService
+import dev.usbharu.hideout.activitypub.application.nodeinfo.NodeinfoRequest
+import dev.usbharu.hideout.core.config.ApplicationConfig
+import dev.usbharu.hideout.core.domain.model.support.principal.Anonymous
+import kotlinx.coroutines.test.runTest
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.mockito.InjectMocks
+import org.mockito.Spy
+import org.mockito.junit.jupiter.MockitoExtension
+import org.springframework.boot.info.BuildProperties
+import util.TestTransaction
+import java.net.URI
+import java.util.*
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+
+@ExtendWith(MockitoExtension::class)
+class NodeinfoApplicationServiceTest {
+    @InjectMocks
+    lateinit var nodeinfoApplicationService: NodeinfoApplicationService
+
+    @Spy
+    val buildInfo: BuildProperties = BuildProperties(Properties())
+
+    @Spy
+    val applicationConfig = ApplicationConfig(URI.create("https://example.com"))
+
+    @Spy
+    val transaction = TestTransaction
+
+    @Test
+    fun nodeinfo2_0() = runTest {
+        val execute = nodeinfoApplicationService.execute(NodeinfoRequest("2.0"), Anonymous)
+
+        assertFalse(execute.openRegistration)
+        assertEquals("2.0", execute.version)
+        assertEquals("hideout", execute.software["name"])
+    }
+
+    @Test
+    fun nodeinfo2_1() = runTest {
+        val execute = nodeinfoApplicationService.execute(NodeinfoRequest("2.1"), Anonymous)
+
+        assertFalse(execute.openRegistration)
+        assertEquals("2.1", execute.version)
+        assertEquals("hideout", execute.software["name"])
+    }
+}
\ No newline at end of file
diff --git a/hideout/hideout-activitypub/src/test/kotlin/wellknown/NodeinfoControllerTest.kt b/hideout/hideout-activitypub/src/test/kotlin/wellknown/NodeinfoControllerTest.kt
new file mode 100644
index 00000000..4c440e57
--- /dev/null
+++ b/hideout/hideout-activitypub/src/test/kotlin/wellknown/NodeinfoControllerTest.kt
@@ -0,0 +1,69 @@
+package wellknown
+
+import dev.usbharu.hideout.SpringApplication
+import org.flywaydb.core.Flyway
+import org.junit.jupiter.api.AfterAll
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.http.MediaType
+import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity
+import org.springframework.test.web.servlet.MockMvc
+import org.springframework.test.web.servlet.get
+import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
+import org.springframework.test.web.servlet.setup.MockMvcBuilders
+import org.springframework.web.context.WebApplicationContext
+
+@SpringBootTest(classes = [SpringApplication::class])
+@AutoConfigureMockMvc
+class NodeinfoControllerTest {
+
+    @Autowired
+    private lateinit var context: WebApplicationContext
+
+    private lateinit var mockMvc: MockMvc
+
+    @BeforeEach
+    fun setUp() {
+        mockMvc = MockMvcBuilders.webAppContextSetup(context)
+            .apply<DefaultMockMvcBuilder>(springSecurity())
+            .build()
+    }
+
+    @Test
+    fun nodeinfo() {
+        mockMvc.get("/.well-known/nodeinfo")
+            .andDo { print() }
+            .andExpect { status { isOk() } }
+            .andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
+    }
+
+    @Test
+    fun nodeinfo2_0() {
+        mockMvc.get("/nodeinfo/2.0")
+            .asyncDispatch()
+            .andDo { print() }
+            .andExpect { status { isOk() } }
+            .andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
+    }
+
+    @Test
+    fun nodeinfo2_1() {
+        mockMvc.get("/nodeinfo/2.1")
+            .asyncDispatch()
+            .andDo { print() }
+            .andExpect { status { isOk() } }
+            .andExpect { content { contentType(MediaType.APPLICATION_JSON) } }
+    }
+
+    companion object {
+        @JvmStatic
+        @AfterAll
+        fun dropDatabase(@Autowired flyway: Flyway) {
+            flyway.clean()
+            flyway.migrate()
+        }
+    }
+}
\ No newline at end of file
diff --git a/hideout/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt b/hideout/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt
index 9a1ea2da..97d62803 100644
--- a/hideout/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt
+++ b/hideout/hideout-core/src/main/kotlin/dev/usbharu/hideout/core/config/SecurityConfig.kt
@@ -90,7 +90,7 @@ class SecurityConfig {
                 authorize("/error", permitAll)
                 authorize("/auth/sign_in", permitAll)
                 authorize(GET, "/.well-known/**", permitAll)
-                authorize(GET, "/nodeinfo/2.0", permitAll)
+                authorize(GET, "/nodeinfo/**", permitAll)
 
                 authorize(GET, "/auth/sign_up", hasRole("ANONYMOUS"))
                 authorize(POST, "/auth/sign_up", permitAll)